diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml old mode 100644 new mode 100755 index 88e2c4a2d435e9dc3f5411daece41c6d5bb39d06..af5398e22a77363683d08ed53da347c594f40b77 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,11 +2,11 @@ # Composer stores all downloaded packages in the vendor/ directory. # Do not use the following if the vendor/ directory is committed to # your git repository. -cache: - # We key the cache using the commit unique identifier. - key: ${CI_COMMIT_REF_SLUG} - paths: - - vendor/ +#cache: +# # We key the cache using the commit unique identifier. +# key: ${CI_COMMIT_REF_SLUG} +# paths: +# - vendor/ # List of stages for jobs, and their order of execution stages: @@ -21,6 +21,7 @@ test-74: image: cicnavi/dap:74 script: - composer install --prefer-dist --no-progress --no-suggest + - vendor/bin/psalm --clear-cache - composer run-script pre-commit # PHP v8.1 @@ -31,6 +32,7 @@ test-81: image: cicnavi/dap:81 script: - composer install --prefer-dist --no-progress --no-suggest + - vendor/bin/psalm --clear-cache - composer run-script pre-commit # TODO mivanci remove when GEANT project finishes diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 diff --git a/composer.lock b/composer.lock index 8cb9d327edc5891640e19808a8f8b4bbfe10a7a9..121a1d684c1947f0ad6b3d1ef513c25442ff6168 100644 --- a/composer.lock +++ b/composer.lock @@ -266,16 +266,16 @@ }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "8cffffb2218e01f3b370bf763e00e81697725259" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/8cffffb2218e01f3b370bf763e00e81697725259", + "reference": "8cffffb2218e01f3b370bf763e00e81697725259", "shasum": "" }, "require": { @@ -303,9 +303,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/v1.1.0" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-05-29T18:55:17+00:00" }, { "name": "doctrine/event-manager", @@ -1083,16 +1083,16 @@ }, { "name": "composer/composer", - "version": "2.5.5", + "version": "2.5.7", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "c7cffaad16a60636a776017eac5bd8cd0095c32f" + "reference": "d477018d3f2ebd76dede3d3988a0b1a7add4d81e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/c7cffaad16a60636a776017eac5bd8cd0095c32f", - "reference": "c7cffaad16a60636a776017eac5bd8cd0095c32f", + "url": "https://api.github.com/repos/composer/composer/zipball/d477018d3f2ebd76dede3d3988a0b1a7add4d81e", + "reference": "d477018d3f2ebd76dede3d3988a0b1a7add4d81e", "shasum": "" }, "require": { @@ -1176,7 +1176,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.5.5" + "source": "https://github.com/composer/composer/tree/2.5.7" }, "funding": [ { @@ -1192,7 +1192,7 @@ "type": "tidelift" } ], - "time": "2023-03-21T10:50:05+00:00" + "time": "2023-05-24T13:00:40+00:00" }, { "name": "composer/metadata-minifier", @@ -2196,21 +2196,21 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.5.1", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9" + "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9", - "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", + "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", @@ -2222,7 +2222,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.29 || ^9.5.23", "psr/log": "^1.1 || ^2.0 || ^3.0" }, @@ -2236,9 +2237,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -2304,7 +2302,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.1" + "source": "https://github.com/guzzle/guzzle/tree/7.7.0" }, "funding": [ { @@ -2320,38 +2318,37 @@ "type": "tidelift" } ], - "time": "2023-04-17T16:30:08+00:00" + "time": "2023-05-21T14:04:53+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", + "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -2388,7 +2385,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/2.0.0" }, "funding": [ { @@ -2404,7 +2401,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2023-05-21T13:50:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -3681,16 +3678,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -3731,9 +3728,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "paragonie/random_compat", @@ -4008,16 +4005,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", "shasum": "" }, "require": { @@ -4060,9 +4057,9 @@ "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.7.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" }, - "time": "2023-03-27T19:02:04+00:00" + "time": "2023-05-30T18:13:47+00:00" }, { "name": "phpmailer/phpmailer", @@ -4146,22 +4143,23 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.20.4", + "version": "1.21.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd" + "reference": "b0c366dd2cea79407d635839d25423ba07c55dd6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", - "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b0c366dd2cea79407d635839d25423ba07c55dd6", + "reference": "b0c366dd2cea79407d635839d25423ba07c55dd6", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -4185,9 +4183,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.3" }, - "time": "2023-05-02T09:19:37+00:00" + "time": "2023-05-29T19:31:28+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4509,16 +4507,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -4592,7 +4590,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -4608,7 +4606,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "psr/container", @@ -6048,16 +6046,16 @@ }, { "name": "seld/jsonlint", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "4211420d25eba80712bff236a98960ef68b866b7" + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", - "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", "shasum": "" }, "require": { @@ -6096,7 +6094,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" }, "funding": [ { @@ -6108,7 +6106,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T13:37:23+00:00" + "time": "2023-05-11T13:16:46+00:00" }, { "name": "seld/phar-utils", @@ -6221,16 +6219,16 @@ }, { "name": "simplesamlphp/saml2", - "version": "v4.6.8", + "version": "v4.6.10", "source": { "type": "git", "url": "https://github.com/simplesamlphp/saml2.git", - "reference": "dea19260955a863f6a347e8479a2049d99b5cd18" + "reference": "a6c46e8134df2686da9ad44bc9b8f85443c03440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/dea19260955a863f6a347e8479a2049d99b5cd18", - "reference": "dea19260955a863f6a347e8479a2049d99b5cd18", + "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/a6c46e8134df2686da9ad44bc9b8f85443c03440", + "reference": "a6c46e8134df2686da9ad44bc9b8f85443c03440", "shasum": "" }, "require": { @@ -6273,22 +6271,22 @@ "description": "SAML2 PHP library from SimpleSAMLphp", "support": { "issues": "https://github.com/simplesamlphp/saml2/issues", - "source": "https://github.com/simplesamlphp/saml2/tree/v4.6.8" + "source": "https://github.com/simplesamlphp/saml2/tree/v4.6.10" }, - "time": "2023-05-05T11:55:46+00:00" + "time": "2023-05-31T16:03:51+00:00" }, { "name": "simplesamlphp/simplesamlphp", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp.git", - "reference": "de912366cb73087889c580dca354582e8ef560e0" + "reference": "7f372865af8317450580373c601315eb1c2e19ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/de912366cb73087889c580dca354582e8ef560e0", - "reference": "de912366cb73087889c580dca354582e8ef560e0", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/7f372865af8317450580373c601315eb1c2e19ad", + "reference": "7f372865af8317450580373c601315eb1c2e19ad", "shasum": "" }, "require": { @@ -6393,7 +6391,7 @@ "issues": "https://github.com/simplesamlphp/simplesamlphp/issues", "source": "https://github.com/simplesamlphp/simplesamlphp" }, - "time": "2023-03-29T20:24:27+00:00" + "time": "2023-05-12T15:57:40+00:00" }, { "name": "simplesamlphp/simplesamlphp-assets-base", @@ -6519,16 +6517,16 @@ }, { "name": "simplesamlphp/simplesamlphp-test-framework", - "version": "v1.5.4", + "version": "v1.5.5", "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp-test-framework.git", - "reference": "b627dd12d1d5bb50cef5336b9726f3a2d1b4969e" + "reference": "98e550a86b3c630820b363575a72b7f7073ef589" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-test-framework/zipball/b627dd12d1d5bb50cef5336b9726f3a2d1b4969e", - "reference": "b627dd12d1d5bb50cef5336b9726f3a2d1b4969e", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-test-framework/zipball/98e550a86b3c630820b363575a72b7f7073ef589", + "reference": "98e550a86b3c630820b363575a72b7f7073ef589", "shasum": "" }, "require": { @@ -6562,7 +6560,7 @@ "issues": "https://github.com/simplesamlphp/simplesamlphp-test-framework/issues", "source": "https://github.com/simplesamlphp/simplesamlphp-test-framework" }, - "time": "2023-03-16T20:42:22+00:00" + "time": "2023-05-20T08:39:41+00:00" }, { "name": "spatie/array-to-xml", @@ -7122,16 +7120,16 @@ }, { "name": "symfony/console", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c" + "reference": "560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/90f21e27d0d88ce38720556dd164d4a1e4c3934c", - "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c", + "url": "https://api.github.com/repos/symfony/console/zipball/560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8", + "reference": "560fc3ed7a43e6d30ea94a07d77f9a60b8ed0fb8", "shasum": "" }, "require": { @@ -7201,7 +7199,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.23" + "source": "https://github.com/symfony/console/tree/v5.4.24" }, "funding": [ { @@ -7217,20 +7215,20 @@ "type": "tidelift" } ], - "time": "2023-04-24T18:47:29+00:00" + "time": "2023-05-26T05:13:16+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "bb7b7988c898c94f5338e16403c52b5a3cae1d93" + "reference": "4645e032d0963fb614969398ca28e47605b1a7da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bb7b7988c898c94f5338e16403c52b5a3cae1d93", - "reference": "bb7b7988c898c94f5338e16403c52b5a3cae1d93", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4645e032d0963fb614969398ca28e47605b1a7da", + "reference": "4645e032d0963fb614969398ca28e47605b1a7da", "shasum": "" }, "require": { @@ -7290,7 +7288,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.23" + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.24" }, "funding": [ { @@ -7306,7 +7304,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T15:04:16+00:00" + "time": "2023-05-05T14:42:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -7377,16 +7375,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "218206b4772d9f412d7d277980c020d06e9d8a4e" + "reference": "c1b9be3b8a6f60f720bec28c4ffb6fb5b00a8946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/218206b4772d9f412d7d277980c020d06e9d8a4e", - "reference": "218206b4772d9f412d7d277980c020d06e9d8a4e", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1b9be3b8a6f60f720bec28c4ffb6fb5b00a8946", + "reference": "c1b9be3b8a6f60f720bec28c4ffb6fb5b00a8946", "shasum": "" }, "require": { @@ -7428,7 +7426,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.23" + "source": "https://github.com/symfony/error-handler/tree/v5.4.24" }, "funding": [ { @@ -7444,7 +7442,7 @@ "type": "tidelift" } ], - "time": "2023-04-17T10:03:27+00:00" + "time": "2023-05-02T16:13:31+00:00" }, { "name": "symfony/event-dispatcher", @@ -7739,16 +7737,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v5.4.22", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "6cb4f6aed4bd7fbf7b2ee74c231184a07f3d00c1" + "reference": "c06a56a47817d29318aaace1c655cbde16c998e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6cb4f6aed4bd7fbf7b2ee74c231184a07f3d00c1", - "reference": "6cb4f6aed4bd7fbf7b2ee74c231184a07f3d00c1", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c06a56a47817d29318aaace1c655cbde16c998e8", + "reference": "c06a56a47817d29318aaace1c655cbde16c998e8", "shasum": "" }, "require": { @@ -7762,7 +7760,7 @@ "symfony/event-dispatcher": "^5.1|^6.0", "symfony/filesystem": "^4.4|^5.0|^6.0", "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", + "symfony/http-foundation": "^5.4.24|^6.2.11", "symfony/http-kernel": "^5.4|^6.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "^1.16", @@ -7775,7 +7773,6 @@ "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "phpunit/phpunit": "<5.4.3", "symfony/asset": "<5.3", "symfony/console": "<5.2.5", "symfony/dom-crawler": "<4.4", @@ -7870,7 +7867,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v5.4.22" + "source": "https://github.com/symfony/framework-bundle/tree/v5.4.24" }, "funding": [ { @@ -7886,20 +7883,20 @@ "type": "tidelift" } ], - "time": "2023-03-31T08:25:44+00:00" + "time": "2023-05-25T13:05:00+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "af9fbb378f5f956c8f29d4886644c84c193780ac" + "reference": "3c59f97f6249ce552a44f01b93bfcbd786a954f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/af9fbb378f5f956c8f29d4886644c84c193780ac", - "reference": "af9fbb378f5f956c8f29d4886644c84c193780ac", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3c59f97f6249ce552a44f01b93bfcbd786a954f5", + "reference": "3c59f97f6249ce552a44f01b93bfcbd786a954f5", "shasum": "" }, "require": { @@ -7946,7 +7943,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.23" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.24" }, "funding": [ { @@ -7962,20 +7959,20 @@ "type": "tidelift" } ], - "time": "2023-04-18T06:30:11+00:00" + "time": "2023-05-19T07:21:23+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "48ea17a7c65ef1ede0c3b2dbc35adace99071810" + "reference": "f38b722e1557eb3f487d351b48f5a1279b50e9d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/48ea17a7c65ef1ede0c3b2dbc35adace99071810", - "reference": "48ea17a7c65ef1ede0c3b2dbc35adace99071810", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f38b722e1557eb3f487d351b48f5a1279b50e9d1", + "reference": "f38b722e1557eb3f487d351b48f5a1279b50e9d1", "shasum": "" }, "require": { @@ -8058,7 +8055,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.23" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.24" }, "funding": [ { @@ -8074,7 +8071,7 @@ "type": "tidelift" } ], - "time": "2023-04-28T13:29:52+00:00" + "time": "2023-05-27T08:06:30+00:00" }, { "name": "symfony/intl", @@ -8737,16 +8734,16 @@ }, { "name": "symfony/process", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4b842fc4b61609e0a155a114082bd94e31e98287" + "reference": "e3c46cc5689c8782944274bb30702106ecbe3b64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4b842fc4b61609e0a155a114082bd94e31e98287", - "reference": "4b842fc4b61609e0a155a114082bd94e31e98287", + "url": "https://api.github.com/repos/symfony/process/zipball/e3c46cc5689c8782944274bb30702106ecbe3b64", + "reference": "e3c46cc5689c8782944274bb30702106ecbe3b64", "shasum": "" }, "require": { @@ -8779,7 +8776,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.23" + "source": "https://github.com/symfony/process/tree/v5.4.24" }, "funding": [ { @@ -8795,7 +8792,7 @@ "type": "tidelift" } ], - "time": "2023-04-18T13:50:24+00:00" + "time": "2023-05-17T11:26:05+00:00" }, { "name": "symfony/routing", @@ -9257,16 +9254,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.4.23", + "version": "v5.4.24", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9a8a5b6d6508928174ded2109e29328a55342a42" + "reference": "8e12706bf9c68a2da633f23bfdc15b4dce5970b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a8a5b6d6508928174ded2109e29328a55342a42", - "reference": "9a8a5b6d6508928174ded2109e29328a55342a42", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/8e12706bf9c68a2da633f23bfdc15b4dce5970b3", + "reference": "8e12706bf9c68a2da633f23bfdc15b4dce5970b3", "shasum": "" }, "require": { @@ -9275,7 +9272,6 @@ "symfony/polyfill-php80": "^1.16" }, "conflict": { - "phpunit/phpunit": "<5.4.3", "symfony/console": "<4.4" }, "require-dev": { @@ -9326,7 +9322,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.23" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.24" }, "funding": [ { @@ -9342,7 +9338,7 @@ "type": "tidelift" } ], - "time": "2023-04-18T09:26:27+00:00" + "time": "2023-05-25T13:05:00+00:00" }, { "name": "symfony/var-exporter", @@ -9818,16 +9814,16 @@ }, { "name": "vimeo/psalm", - "version": "5.11.0", + "version": "5.12.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "c9b192ab8400fdaf04b2b13d110575adc879aa90" + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/c9b192ab8400fdaf04b2b13d110575adc879aa90", - "reference": "c9b192ab8400fdaf04b2b13d110575adc879aa90", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176", + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176", "shasum": "" }, "require": { @@ -9918,9 +9914,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.11.0" + "source": "https://github.com/vimeo/psalm/tree/5.12.0" }, - "time": "2023-05-04T21:35:44+00:00" + "time": "2023-05-22T21:19:03+00:00" }, { "name": "web-token/jwt-framework", diff --git a/config-templates/module_accounting.php b/config-templates/module_accounting.php old mode 100644 new mode 100755 index a1cc2e7bdc502571235898285f7ba1c48616c2b9..69a9027adaf88b869070fa49ba29857a581ade07 --- a/config-templates/module_accounting.php +++ b/config-templates/module_accounting.php @@ -72,10 +72,9 @@ $config = [ ModuleConfiguration::OPTION_PROVIDER_FOR_CONNECTED_SERVICES => /** * Default connected services provider which expects Doctrine DBAL compatible connection to be set below. - * CurrentDataProvider only gathers current (latest information) about the service and user (there is no - * versioning, so it's faster). VersionedDataProvider keeps track of any changes in data about the service - * and user. - * + * CurrentDataProvider only gathers current (latest information) about the service (there is no + * versioning, so it's faster). VersionedDataProvider keeps track of any changes in data about + * the service. */ Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider::class, //Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider::class, @@ -91,9 +90,12 @@ $config = [ ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY => /** * Default activity provider which expects Doctrine DBAL compatible connection to be set below. - * Currently only VersionedDataProvider is available, which tracks all changes in services and users. + * CurrentDataProvider only gathers current (latest information) about the service (there is no + * versioning, so it's faster). VersionedDataProvider keeps track of any changes in data about + * the service. */ - Providers\Activity\DoctrineDbal\VersionedDataProvider::class, + Providers\Activity\DoctrineDbal\CurrentDataProvider::class, + //Providers\Activity\DoctrineDbal\VersionedDataProvider::class, /** * Trackers @@ -134,6 +136,12 @@ $config = [ 'doctrine_dbal_pdo_mysql', ], ], + Providers\Activity\DoctrineDbal\CurrentDataProvider::class => [ + ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_mysql', + ModuleConfiguration\ConnectionType::SLAVE => [ + 'doctrine_dbal_pdo_mysql', + ], + ], Providers\Activity\DoctrineDbal\VersionedDataProvider::class => [ ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_mysql', ModuleConfiguration\ConnectionType::SLAVE => [ diff --git a/hooks/hook_adminmenu.php b/hooks/hook_adminmenu.php old mode 100644 new mode 100755 diff --git a/hooks/hook_configpage.php b/hooks/hook_configpage.php old mode 100644 new mode 100755 diff --git a/hooks/hook_cron.php b/hooks/hook_cron.php old mode 100644 new mode 100755 diff --git a/locales/en/LC_MESSAGES/accounting.po b/locales/en/LC_MESSAGES/accounting.po old mode 100644 new mode 100755 diff --git a/locales/hr/LC_MESSAGES/accounting.po b/locales/hr/LC_MESSAGES/accounting.po old mode 100644 new mode 100755 diff --git a/phpcs.xml b/phpcs.xml old mode 100644 new mode 100755 diff --git a/phpunit.xml b/phpunit.xml old mode 100644 new mode 100755 diff --git a/psalm.xml b/psalm.xml old mode 100644 new mode 100755 index 618c01a269b1d4cf27637ff4bba24ed200405b90..62ecf4e6ac338eb82d79a576194f80399f57344b --- a/psalm.xml +++ b/psalm.xml @@ -7,6 +7,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + cacheDirectory="./build/psalm/cache" > <projectFiles> <directory name="src" /> diff --git a/public/assets/css/src/custom.css b/public/assets/css/src/custom.css old mode 100644 new mode 100755 diff --git a/public/assets/css/src/default.css b/public/assets/css/src/default.css old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-300.woff b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-300.woff old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-300.woff2 b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-300.woff2 old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-500.woff b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-500.woff old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-500.woff2 b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-500.woff2 old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-600.woff b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-600.woff old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-600.woff2 b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-600.woff2 old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-regular.woff b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-regular.woff old mode 100644 new mode 100755 diff --git a/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-regular.woff2 b/public/assets/css/src/fonts/raleway-v28-latin-ext_latin-regular.woff2 old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/activity.svg b/public/assets/css/src/icons/activity.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/conn-orgs.svg b/public/assets/css/src/icons/conn-orgs.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/download.svg b/public/assets/css/src/icons/download.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/dropdown.svg b/public/assets/css/src/icons/dropdown.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/fppp-logo.svg b/public/assets/css/src/icons/fppp-logo.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/i.svg b/public/assets/css/src/icons/i.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/logout.svg b/public/assets/css/src/icons/logout.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/prof-page.svg b/public/assets/css/src/icons/prof-page.svg old mode 100644 new mode 100755 diff --git a/public/assets/css/src/icons/x.svg b/public/assets/css/src/icons/x.svg old mode 100644 new mode 100755 diff --git a/routing/routes/routes.yml b/routing/routes/routes.yml old mode 100644 new mode 100755 diff --git a/routing/services/services.yml b/routing/services/services.yml old mode 100644 new mode 100755 diff --git a/src/Auth/Process/Accounting.php b/src/Auth/Process/Accounting.php old mode 100644 new mode 100755 diff --git a/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProvider.php b/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..a38b42baff5512d189c53c9901288de3d43c9e32 --- /dev/null +++ b/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProvider.php @@ -0,0 +1,95 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal; + +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ActivityInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\CurrentDataTracker; +use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; +use SimpleSAML\Module\accounting\Entities\Activity; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; + +class CurrentDataProvider implements ActivityInterface +{ + protected ModuleConfiguration $moduleConfiguration; + protected LoggerInterface $logger; + protected Store $store; + + /** + * @throws StoreException + */ + public function __construct( + ModuleConfiguration $moduleConfiguration, + LoggerInterface $logger, + string $connectionType = ModuleConfiguration\ConnectionType::SLAVE, + Store $store = null + ) { + $this->moduleConfiguration = $moduleConfiguration; + $this->logger = $logger; + + $this->store = $store ?? new Store( + $this->moduleConfiguration, + $this->logger, + $this->moduleConfiguration->getClassConnectionKey(self::class), + $connectionType + ); + } + + /** + * @throws StoreException + */ + public static function build( + ModuleConfiguration $moduleConfiguration, + LoggerInterface $logger, + string $connectionType = ModuleConfiguration\ConnectionType::SLAVE + ): self { + return new self($moduleConfiguration, $logger, $connectionType); + } + + /** + * @throws StoreException + */ + public function needsSetup(): bool + { + return $this->store->needsSetup(); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function runSetup(): void + { + if (! $this->needsSetup()) { + $this->logger->warning('Run setup called, however setup is not needed.'); + return; + } + + $this->store->runSetup(); + } + + /** + * @throws StoreException + */ + public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag + { + return $this->store->getActivity($userIdentifier, $maxResults, $firstResult); + } + + /** + * @throws StoreException + */ + public function getTracker(): ?DataTrackerInterface + { + return new CurrentDataTracker( + $this->moduleConfiguration, + $this->logger, + ModuleConfiguration\ConnectionType::MASTER, + ); + } +} diff --git a/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php b/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php old mode 100644 new mode 100755 diff --git a/src/Data/Providers/Builders/DataProviderBuilder.php b/src/Data/Providers/Builders/DataProviderBuilder.php old mode 100644 new mode 100755 index 7bba072c97be2b569875315bab0c6ea85af885bf..d8f0fd5fafe73ed1622da6ee934429069c7a11c3 --- a/src/Data/Providers/Builders/DataProviderBuilder.php +++ b/src/Data/Providers/Builders/DataProviderBuilder.php @@ -64,6 +64,9 @@ class DataProviderBuilder return $provider; } + /** + * @throws Exception + */ public function buildActivityProvider( string $class, string $connectionType = ModuleConfiguration\ConnectionType::SLAVE diff --git a/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php b/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php old mode 100644 new mode 100755 diff --git a/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php b/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php old mode 100644 new mode 100755 diff --git a/src/Data/Providers/Interfaces/ActivityInterface.php b/src/Data/Providers/Interfaces/ActivityInterface.php old mode 100644 new mode 100755 index 0102c89b48cd3cde707c11769b5c0251b2f7cffd..906727ecb8ca77a538fc741ea44d6d826b1d9cf8 --- a/src/Data/Providers/Interfaces/ActivityInterface.php +++ b/src/Data/Providers/Interfaces/ActivityInterface.php @@ -6,8 +6,6 @@ namespace SimpleSAML\Module\accounting\Data\Providers\Interfaces; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Entities\Activity; -use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface; -use SimpleSAML\Module\accounting\Interfaces\SetupableInterface; use SimpleSAML\Module\accounting\ModuleConfiguration; interface ActivityInterface extends DataProviderInterface diff --git a/src/Data/Providers/Interfaces/ConnectedServicesInterface.php b/src/Data/Providers/Interfaces/ConnectedServicesInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Providers/Interfaces/DataProviderInterface.php b/src/Data/Providers/Interfaces/DataProviderInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store.php new file mode 100644 index 0000000000000000000000000000000000000000..39ea7df96538013b9a82205e146609157086adcc --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store.php @@ -0,0 +1,97 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current; + +use DateTimeImmutable; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Store\GettableActivityTrait; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store as BaseStore; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; +use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ActivityInterface; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Module\accounting\Services\HelpersManager; + +class Store extends BaseStore implements ActivityInterface +{ + use GettableActivityTrait; + + protected Repository $repository; + + /** + * @throws StoreException + */ + public function __construct( + ModuleConfiguration $moduleConfiguration, + LoggerInterface $logger, + string $connectionKey = null, + string $connectionType = ModuleConfiguration\ConnectionType::MASTER, + Factory $connectionFactory = null, + HelpersManager $helpersManager = null, + Repository $repository = null + ) { + parent::__construct( + $moduleConfiguration, + $logger, + $connectionKey, + $connectionType, + $connectionFactory, + $helpersManager, + $repository + ); + + $this->repository = $repository ?? new Repository($this->connection, $this->logger); + } + + /** + * Build store instance. + * @throws StoreException + */ + public static function build( + ModuleConfiguration $moduleConfiguration, + LoggerInterface $logger, + string $connectionKey = null, + string $connectionType = ModuleConfiguration\ConnectionType::MASTER + ): self { + return new self( + $moduleConfiguration, + $logger, + $connectionKey, + $connectionType + ); + } + + /** + * @throws StoreException + */ + public function persist(Event $authenticationEvent): void + { + $hashDecoratedState = new HashDecoratedState($authenticationEvent->getState()); + + $spId = $this->resolveSpId($hashDecoratedState); + $userId = $this->resolveUserId($hashDecoratedState); + $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState); + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $authenticationEvent->getHappenedAt(), + $authenticationEvent->getState()->getClientIpAddress(), + $authenticationEvent->getState()->getAuthenticationProtocol()->getDesignation() + ); + } + + /** + * @throws StoreException + */ + public function deleteDataOlderThan(DateTimeImmutable $dateTime): void + { + // Only delete authentication events. + $this->repository->deleteAuthenticationEventsOlderThan($dateTime); + } +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000200CreateSpTable.php new file mode 100644 index 0000000000000000000000000000000000000000..3b295f48036e413b5adf7d84c5b15f1ef2c26f3e --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000200CreateSpTable.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable; + +class Version20220801000200CreateSpTable extends CreateSpTable +{ +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000400CreateUserTable.php new file mode 100644 index 0000000000000000000000000000000000000000..1aa8c3c2394396f021c1d66127b140a725bbe7b2 --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000400CreateUserTable.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable; + +class Version20220801000400CreateUserTable extends CreateUserTable +{ +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000500CreateUserVersionTable.php new file mode 100644 index 0000000000000000000000000000000000000000..7cf49583c2bddadc2883bbe4eb69796ba9d88174 --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000500CreateUserVersionTable.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; + +class Version20220801000500CreateUserVersionTable extends Migrations\CreateUserVersionTable +{ +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php new file mode 100644 index 0000000000000000000000000000000000000000..6ac9a2b2c1f7985eecf0415b3573831d94c4dc3b --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php @@ -0,0 +1,98 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Types; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use Throwable; + +use function sprintf; + +class Version20220801000700CreateAuthenticationEventTable extends AbstractMigration +{ + protected function getLocalTablePrefix(): string + { + return 'cds_'; + } + + /** + * @inheritDoc + * @throws MigrationException + */ + public function run(): void + { + $tableName = $this->preparePrefixedTableName('authentication_event'); + + try { + $table = new Table($tableName); + + $table->addColumn('id', Types::BIGINT) + ->setUnsigned(true) + ->setAutoincrement(true); + + $table->addColumn('sp_id', Types::BIGINT) + ->setUnsigned(true); + + $table->addColumn('user_version_id', Types::BIGINT) + ->setUnsigned(true); + + $table->addColumn('happened_at', Types::DATETIMETZ_IMMUTABLE); + + $table->addColumn('client_ip_address', Types::STRING) + ->setLength(BaseTableConstantsAlias::COLUMN_IP_ADDRESS_LENGTH) + ->setNotnull(false); + + $table->addColumn('authentication_protocol_designation', Types::STRING) + ->setLength(BaseTableConstantsAlias::COLUMN_AUTHENTICATION_PROTOCOL_DESIGNATION_LENGTH) + ->setNotnull(false); + + $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); + + $table->setPrimaryKey(['id']); + + $table->addForeignKeyConstraint( + $this->preparePrefixedTableName('sp'), + ['sp_id'], + ['id'] + ); + + // We are using versioned data for user management. + $versionedDataStoreTablePrefix = 'vds_'; + $table->addForeignKeyConstraint( + $this->preparePrefixedTableName('user_version', $versionedDataStoreTablePrefix), + ['user_version_id'], + ['id'] + ); + + // Old data can be deleted using happened_at column, so add index for it. + $table->addIndex(['happened_at']); + + $this->schemaManager->createTable($table); + } catch (Throwable $exception) { + throw $this->prepareGenericMigrationException( + sprintf('Error creating table \'%s.', $tableName), + $exception + ); + } + } + + /** + * @inheritDoc + * @throws MigrationException + */ + public function revert(): void + { + $tableName = $this->preparePrefixedTableName('authentication_event'); + + try { + $this->schemaManager->dropTable($tableName); + } catch (Throwable $exception) { + throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Repository.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Repository.php new file mode 100644 index 0000000000000000000000000000000000000000..c6a9ca0f9c69ad63ec0bff3af80dfd10a15e7001 --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Repository.php @@ -0,0 +1,204 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; + +use DateTimeImmutable; +use Doctrine\DBAL\Types\Types; +use Psr\Log\LoggerInterface; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Repository\DeletableAuthenticationEventsTrait; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository as BaseRepository; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedBaseTableconstants; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants; + +class Repository extends BaseRepository +{ + use DeletableAuthenticationEventsTrait; + + protected string $tableNameAuthenticationEvent; + + public function __construct(Connection $connection, LoggerInterface $logger) + { + parent::__construct($connection, $logger); + + $this->tableNameAuthenticationEvent = + $this->preparePrefixedTableName( + TableConstants::TABLE_NAME_AUTHENTICATION_EVENT + ); + } + + /** + * @throws StoreException + */ + public function insertAuthenticationEvent( + int $spId, + int $userVersionId, + DateTimeImmutable $happenedAt, + string $clientIpAddress = null, + string $authenticationProtocolDesignation = null, + DateTimeImmutable $createdAt = null + ): void { + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $createdAt = $createdAt ?? new DateTimeImmutable(); + + $queryBuilder->insert($this->tableNameAuthenticationEvent) + ->values( + [ + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID => ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID => ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => + ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID => + $spId, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID => + $userVersionId, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => $happenedAt, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => $clientIpAddress, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => + $authenticationProtocolDesignation, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => $createdAt, + ], + [ + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID => Types::BIGINT, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => + Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => + Types::STRING, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => + Types::STRING, + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => + Types::DATETIMETZ_IMMUTABLE, + ] + ); + + $queryBuilder->executeStatement(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to insert AuthenticationEvent. Error was: %s.', + $exception->getMessage() + ); + $this->logger->error($message, compact('spId', 'userVersionId')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } + + /** + * @throws StoreException + */ + public function getActivity(string $userIdentifierHashSha256, int $maxResults, int $firstResult): array + { + try { + $authenticationEventsQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + + /** @psalm-suppress TooManyArguments */ + $authenticationEventsQueryBuilder->select( + //'cae.happened_at', + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT, + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS, + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION, + //'cs.metadata AS sp_metadata', + BaseTableConstants::TABLE_ALIAS_SP . '.' . + BaseTableConstants::TABLE_SP_COLUMN_NAME_METADATA . + ' AS ' . EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA, + //'cuv.attributes AS user_attributes' + VersionedBaseTableconstants::TABLE_ALIAS_USER_VERSION . '.' . + VersionedBaseTableconstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' . + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES + )->from( + //'cds_authentication_event', 'cae' + $this->tableNameAuthenticationEvent, + //'cae' + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT + ) + ->leftJoin( + //'cae', + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT, + //'cds_sp', + $this->tableNameSp, + //'vs', + BaseTableConstants::TABLE_ALIAS_SP, + //'cae.sp_id = cs.id' + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID . ' = ' . + BaseTableConstants::TABLE_ALIAS_SP . '.' . + BaseTableConstants::TABLE_SP_COLUMN_NAME_ID + ) + ->leftJoin( + //'cae', + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT, + //'vds_user_version', + $this->tableNameUserVersion, + //'vuv', + VersionedBaseTableconstants::TABLE_ALIAS_USER_VERSION, + //'cae.user_version_id = vuv.id' + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID . ' = ' . + VersionedBaseTableconstants::TABLE_ALIAS_USER_VERSION . '.' . + VersionedBaseTableconstants::TABLE_USER_VERSION_COLUMN_NAME_ID + ) + ->leftJoin( + //'vuv', + VersionedBaseTableconstants::TABLE_ALIAS_USER_VERSION, + //'vds_user', + $this->tableNameUser, + //'vu', + VersionedBaseTableconstants::TABLE_ALIAS_USER, + //'vuv.user_id = vu.id' + VersionedBaseTableconstants::TABLE_ALIAS_USER_VERSION . '.' . + VersionedBaseTableconstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' . + VersionedBaseTableconstants::TABLE_ALIAS_USER . '.' . + VersionedBaseTableconstants::TABLE_USER_COLUMN_NAME_ID + ) + ->where( + //'vu.identifier_hash_sha256 = ' . + VersionedBaseTableconstants::TABLE_ALIAS_USER . '.' . + VersionedBaseTableconstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' . + $authenticationEventsQueryBuilder->createNamedParameter($userIdentifierHashSha256) + ) + ->orderBy( + //'cae.id', + TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' . + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID, + 'DESC' + ) + ->setMaxResults($maxResults) + ->setFirstResult($firstResult); + + return $authenticationEventsQueryBuilder->executeQuery()->fetchAllAssociative(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to get connected organizations. Error was: %s.', + $exception->getMessage() + ); + $this->logger->error($message, compact('userIdentifierHashSha256')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/TableConstants.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/TableConstants.php new file mode 100644 index 0000000000000000000000000000000000000000..b2dde0f8c0db501a641bbf6bb4bb56f283f06b2c --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/TableConstants.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants + as BaseTableConstants; + +class TableConstants +{ + // Table 'authentication_event'. + public const TABLE_NAME_AUTHENTICATION_EVENT = 'authentication_event'; + public const TABLE_ALIAS_AUTHENTICATION_EVENT = BaseTableConstants::TABLE_PREFIX . 'ae'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID = 'id'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_SP_ID = 'sp_id'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_USER_VERSION_ID = 'user_version_id'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT = 'happened_at'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION = + 'authentication_protocol_designation'; + public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT = 'created_at'; +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/EntityTableConstants.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/EntityTableConstants.php new file mode 100644 index 0000000000000000000000000000000000000000..be31816908062e17a977a4a076a7362ea733f28c --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/EntityTableConstants.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal; + +class EntityTableConstants +{ + // Entity 'Activity' related. + public const ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA = 'sp_metadata'; + public const ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes'; + public const ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT = 'happened_at'; + public const ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address'; + public const ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION = + 'authentication_protocol_designation'; +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivity.php old mode 100644 new mode 100755 similarity index 70% rename from src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php rename to src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivity.php index ed7b42cba9e6d162f0f6b194f92508eb186fdc98..2261afa604d211fe05a11a68821c2fe09b111989 --- a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivity.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store; +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal; use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; @@ -22,25 +22,25 @@ class RawActivity extends AbstractRawEntity parent::__construct($rawRow, $abstractPlatform); $this->serviceProviderMetadata = $this->resolveServiceProviderMetadata( - (string)$rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] + (string)$rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] ); $this->userAttributes = $this->resolveUserAttributes( - (string)$rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] + (string)$rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] ); $this->happenedAt = $this->resolveDateTimeImmutable( - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT] + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT] ); - $this->clientIpAddress = empty($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]) ? + $this->clientIpAddress = empty($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]) ? null : - (string)$rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]; + (string)$rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]; $this->authenticationProtocolDesignation = - empty($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]) ? + empty($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]) ? null : - (string)$rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]; + (string)$rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]; } /** @@ -89,9 +89,9 @@ class RawActivity extends AbstractRawEntity protected function validate(array $rawRow): void { $columnsToCheck = [ - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA, - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES, - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT, + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA, + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES, + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT, ]; foreach ($columnsToCheck as $column) { @@ -101,26 +101,26 @@ class RawActivity extends AbstractRawEntity } /** @noinspection DuplicatedCode */ - if (! is_string($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA ); throw new UnexpectedValueException($message); } - if (! is_string($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES ); throw new UnexpectedValueException($message); } - if (! is_string($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT ); throw new UnexpectedValueException($message); } diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Repository/DeletableAuthenticationEventsTrait.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Repository/DeletableAuthenticationEventsTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..538a96c44080b9da465a814a95d0bc4875add91a --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Repository/DeletableAuthenticationEventsTrait.php @@ -0,0 +1,37 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Repository; + +use DateTimeImmutable; +use Doctrine\DBAL\Types\Types; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; + +trait DeletableAuthenticationEventsTrait +{ + /** + * @throws StoreException + */ + public function deleteAuthenticationEventsOlderThan(DateTimeImmutable $dateTime): void + { + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $queryBuilder->delete($this->tableNameAuthenticationEvent) + ->where( + $queryBuilder->expr()->lt( + TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT, + $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE) + ) + )->executeStatement(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to delete old authentication events. Error was: %s.', + $exception->getMessage() + ); + $this->logger->error($message); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Store/GettableActivityTrait.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Store/GettableActivityTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..a2fe28293bc1c6c38d2a8526fa1f906c4a71f585 --- /dev/null +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Traits/Store/GettableActivityTrait.php @@ -0,0 +1,57 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Store; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\RawActivity; +use SimpleSAML\Module\accounting\Entities\Activity; +use SimpleSAML\Module\accounting\Entities\User; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; + +trait GettableActivityTrait +{ + /** + * @throws StoreException + */ + public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag + { + $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier); + + $results = $this->repository->getActivity($userIdentifierHashSha256, $maxResults, $firstResult); + + $activityBag = new Activity\Bag(); + + if (empty($results)) { + return $activityBag; + } + + try { + /** @var array $result */ + foreach ($results as $result) { + $rawActivity = new RawActivity($result, $this->connection->dbal()->getDatabasePlatform()); + $serviceProvider = $this->helpersManager + ->getProviderResolver() + ->forServiceFromMetadataArray($rawActivity->getServiceProviderMetadata()); + $user = new User($rawActivity->getUserAttributes()); + + $activityBag->add( + new Activity( + $serviceProvider, + $user, + $rawActivity->getHappenedAt(), + $rawActivity->getClientIpAddress(), + $rawActivity->getAuthenticationProtocolDesignation() + ) + ); + } + } catch (Throwable $exception) { + $message = sprintf( + 'Error populating activity bag. Error was: %s', + $exception->getMessage() + ); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + + return $activityBag; + } +} diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php old mode 100644 new mode 100755 index 149b8bca2b4f74c4707bdbdbeb1afd7e7864e7a0..c25864c1d72d72919d78116da19e5aa0a484b7d1 --- a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php @@ -6,22 +6,21 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineD use DateTimeImmutable; use Psr\Log\LoggerInterface; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Store\GettableActivityTrait; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store as BaseStore; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ActivityInterface; -use SimpleSAML\Module\accounting\Entities\Activity; use SimpleSAML\Module\accounting\Entities\Authentication\Event; -use SimpleSAML\Module\accounting\Entities\User; use SimpleSAML\Module\accounting\Exceptions\StoreException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; -use Throwable; class Store extends BaseStore implements ActivityInterface { + use GettableActivityTrait; + protected Repository $repository; /** @@ -90,51 +89,6 @@ class Store extends BaseStore implements ActivityInterface ); } - /** - * @throws StoreException - */ - public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag - { - $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier); - - $results = $this->repository->getActivity($userIdentifierHashSha256, $maxResults, $firstResult); - - $activityBag = new Activity\Bag(); - - if (empty($results)) { - return $activityBag; - } - - try { - /** @var array $result */ - foreach ($results as $result) { - $rawActivity = new RawActivity($result, $this->connection->dbal()->getDatabasePlatform()); - $serviceProvider = $this->helpersManager - ->getProviderResolver() - ->forServiceFromMetadataArray($rawActivity->getServiceProviderMetadata()); - $user = new User($rawActivity->getUserAttributes()); - - $activityBag->add( - new Activity( - $serviceProvider, - $user, - $rawActivity->getHappenedAt(), - $rawActivity->getClientIpAddress(), - $rawActivity->getAuthenticationProtocolDesignation() - ) - ); - } - } catch (Throwable $exception) { - $message = sprintf( - 'Error populating activity bag. Error was: %s', - $exception->getMessage() - ); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - return $activityBag; - } - /** * @throws StoreException */ diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php old mode 100644 new mode 100755 index d477b56c40326e5cc546c33aaba8b098c4a7b839..42b14ba1ca4225b9104f4b53e10dc0579f8a3b6b --- a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineD use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -41,11 +41,11 @@ class Version20220801000700CreateAuthenticationEventTable extends AbstractMigrat $table->addColumn('happened_at', Types::DATETIMETZ_IMMUTABLE); $table->addColumn('client_ip_address', Types::STRING) - ->setLength(TableConstants::COLUMN_IP_ADDRESS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_IP_ADDRESS_LENGTH) ->setNotnull(false); $table->addColumn('authentication_protocol_designation', Types::STRING) - ->setLength(TableConstants::COLUMN_AUTHENTICATION_PROTOCOL_DESIGNATION_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_AUTHENTICATION_PROTOCOL_DESIGNATION_LENGTH) ->setNotnull(false); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php old mode 100644 new mode 100755 index 7071f0095aae17cde4e04b0d3c709c56d64f1da9..c47e8a62e6bd9b3512c28b12cdd97563c73f2f09 --- a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php @@ -7,15 +7,20 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineD use DateTimeImmutable; use Doctrine\DBAL\Types\Types; use Psr\Log\LoggerInterface; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Traits\Repository\DeletableAuthenticationEventsTrait; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository as BaseRepository; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as BaseTableConstants; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Exceptions\StoreException; use Throwable; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants; class Repository extends BaseRepository { + use DeletableAuthenticationEventsTrait; + protected string $tableNameAuthenticationEvent; public function __construct(Connection $connection, LoggerInterface $logger) @@ -114,11 +119,11 @@ class Repository extends BaseRepository //'vsv.metadata AS sp_metadata', BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA . - ' AS ' . TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA, + ' AS ' . EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA, //'vuv.attributes AS user_attributes' BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' . - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES )->from( //'vds_authentication_event', 'vae' $this->tableNameAuthenticationEvent, @@ -215,29 +220,4 @@ class Repository extends BaseRepository throw new StoreException($message, (int)$exception->getCode(), $exception); } } - - /** - * @throws StoreException - */ - public function deleteAuthenticationEventsOlderThan(DateTimeImmutable $dateTime): void - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $queryBuilder->delete($this->tableNameAuthenticationEvent) - ->where( - $queryBuilder->expr()->lt( - TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT, - $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE) - ) - )->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to delete old authentication events. Error was: %s.', - $exception->getMessage() - ); - $this->logger->error($message); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } } diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php old mode 100644 new mode 100755 index df5b36c75d946c921b31f98f26f0358903be457b..dcfdaa15ff1f1fcda645ba63bc014394b8fdafeb --- a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php +++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php @@ -19,12 +19,4 @@ class TableConstants public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION = 'authentication_protocol_designation'; public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT = 'created_at'; - - // Entity 'Activity' related. - public const ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA = 'sp_metadata'; - public const ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes'; - public const ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT = 'happened_at'; - public const ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address'; - public const ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION = - 'authentication_protocol_designation'; } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php old mode 100644 new mode 100755 index 0090783a3631b033a6cf8aa1589180587cdd7066..940654664c8483ae946c84247c18742ec610b246 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php @@ -7,18 +7,20 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store as VersionedStore; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface; use SimpleSAML\Module\accounting\Exceptions\StoreException; -use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; use Throwable; class Store extends AbstractStore implements StoreInterface { + use VersionedStore\UserVersionResolvingTrait; + protected HelpersManager $helpersManager; private Repository $repository; @@ -125,118 +127,4 @@ class Store extends AbstractStore implements StoreInterface throw new StoreException($message, (int)$exception->getCode(), $exception); } } - - /** - * @throws StoreException - */ - public function resolveUserId(HashDecoratedState $hashDecoratedState): int - { - $userIdentifierAttributeName = $this->moduleConfiguration->getUserIdAttributeName(); - - $userIdentifierValue = $hashDecoratedState->getState()->getFirstAttributeValue($userIdentifierAttributeName); - if ($userIdentifierValue === null) { - $message = sprintf('Attributes do not contain user ID attribute %s.', $userIdentifierAttributeName); - throw new UnexpectedValueException($message); - } - - $userIdentifierValueHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifierValue); - - // Check if it already exists. - try { - $result = $this->repository->getUser($userIdentifierValueHashSha256); - $userId = $result->fetchOne(); - - if ($userId !== false) { - return (int)$userId; - } - } catch (Throwable $exception) { - $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - // Create new - try { - $this->repository->insertUser($userIdentifierValue, $userIdentifierValueHashSha256); - } catch (Throwable $exception) { - $message = sprintf( - 'Error inserting new user, however, continuing in case of race condition. Error was: %s.', - $exception->getMessage() - ); - $this->logger->warning($message); - } - - // Try again, this time it should exist... - try { - $result = $this->repository->getUser($userIdentifierValueHashSha256); - $userIdNew = $result->fetchOne(); - - if ($userIdNew !== false) { - return (int)$userIdNew; - } - - $message = sprintf( - 'Error fetching user even after insertion for identifier value hash SHA256 %s.', - $userIdentifierValueHashSha256 - ); - throw new StoreException($message); - } catch (Throwable $exception) { - $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int - { - $attributeArrayHashSha256 = $hashDecoratedState->getAttributesArrayHashSha256(); - - // Check if it already exists. - try { - $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); - $userVersionId = $result->fetchOne(); - - if ($userVersionId !== false) { - return (int)$userVersionId; - } - } catch (Throwable $exception) { - $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - // Create new - try { - $this->repository->insertUserVersion( - $userId, - serialize($hashDecoratedState->getState()->getAttributes()), - $attributeArrayHashSha256 - ); - } catch (Throwable $exception) { - $message = sprintf( - 'Error inserting new user version, however, continuing in case of race condition. Error was: %s.', - $exception->getMessage() - ); - $this->logger->warning($message); - } - - // Try again, this time it should exist... - try { - $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); - $userVersionIdNew = $result->fetchOne(); - - if ($userVersionIdNew !== false) { - return (int)$userVersionIdNew; - } - - $message = sprintf( - 'Error fetching user version even after insertion for user ID %s.', - $userId - ); - throw new StoreException($message); - } catch (Throwable $exception) { - $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php old mode 100644 new mode 100755 index 840cd8fbe8c9b98870bb6b36ff8f035ea6a4f314..1d3769a0fd6b63752ee015b588a93dab787653e0 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -41,16 +41,16 @@ class CreateIdpTable extends AbstractMigration ->setAutoincrement(true); $table->addColumn('entity_id', Types::STRING) - ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH); + ->setLength(BaseTableConstantsAlias::COLUMN_ENTITY_ID_LENGTH); $table->addColumn('entity_id_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('metadata', Types::TEXT); $table->addColumn('metadata_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php old mode 100644 new mode 100755 index 5551faf0e6b12907fbb4b1d8d0e02e6b53174a30..7bc5142fce81a913b3882aa8c06c0bb08f8a741c --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -41,16 +41,16 @@ class CreateSpTable extends AbstractMigration ->setAutoincrement(true); $table->addColumn('entity_id', Types::STRING) - ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH); + ->setLength(BaseTableConstantsAlias::COLUMN_ENTITY_ID_LENGTH); $table->addColumn('entity_id_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('metadata', Types::TEXT); $table->addColumn('metadata_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php old mode 100644 new mode 100755 index 090357d69e9484499309c44a5ca737c4f62b8496..7dee55c3c0e0b730bc171310d81a401340b02504 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php @@ -4,75 +4,11 @@ declare(strict_types=1); namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; -use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; -use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; -use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; -use Throwable; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations; -use function sprintf; - -class CreateUserTable extends AbstractMigration +/** + * We use versioned data to manage users, so we reuse versioned user table definitions. + */ +class CreateUserTable extends Migrations\CreateUserTable { - protected function getLocalTablePrefix(): string - { - return 'cds_'; - } - - /** - * @inheritDoc - * @throws MigrationException - */ - public function run(): void - { - $tableName = $this->preparePrefixedTableName('user'); - - try { - if ($this->schemaManager->tablesExist($tableName)) { - return; - } - - $table = new Table($tableName); - - $table->addColumn('id', Types::BIGINT) - ->setUnsigned(true) - ->setAutoincrement(true); - - $table->addColumn('identifier', Types::TEXT) - ->setLength(65535); - - $table->addColumn('identifier_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) - ->setFixed(true); - - $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); - - $table->setPrimaryKey(['id']); - - $table->addUniqueConstraint(['identifier_hash_sha256']); - - $this->schemaManager->createTable($table); - } catch (Throwable $exception) { - throw $this->prepareGenericMigrationException( - sprintf('Error creating table \'%s.', $tableName), - $exception - ); - } - } - - /** - * @inheritDoc - * @throws MigrationException - */ - public function revert(): void - { - $tableName = $this->preparePrefixedTableName('user'); - - try { - $this->schemaManager->dropTable($tableName); - } catch (Throwable $exception) { - throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception); - } - } } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php old mode 100644 new mode 100755 index 11f7926b19a3dadbfcc8fcd74917f90fb8304311..e0975592614fe4ad870e0b10c3fe32e937a899ab --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php @@ -6,10 +6,9 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations; +/** + * We use versioned data to manage users, so we reuse versioned user table definitions. + */ class CreateUserVersionTable extends Migrations\CreateUserVersionTable { - protected function getLocalTablePrefix(): string - { - return 'cds_'; - } } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php old mode 100644 new mode 100755 index 7ec179ccefcacbee5ca94ad50a9c9afa03dd3ecf..7a83da64b2b7f06c24680831f457f37ba94336ec --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php @@ -5,16 +5,22 @@ declare(strict_types=1); namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store; use DateTimeImmutable; -use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Types; use Psr\Log\LoggerInterface; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository as VersionedRepository; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Exceptions\StoreException; use Throwable; class Repository { + // We user versioned user data, so let's reuse versioned user tables. + use VersionedRepository\UserVersionManagementTrait; + protected Connection $connection; protected LoggerInterface $logger; protected string $tableNameIdp; @@ -29,13 +35,23 @@ class Repository $this->tableNameIdp = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_IDP); $this->tableNameSp = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_SP); - $this->tableNameUser = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER); - $this->tableNameUserVersion = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER_VERSION); + + // For user management we use versioned data, so we will reuse tables from versioned data store. + $versionedDataStoreTablePrefix = VersionedTableConstantsAlias::TABLE_PREFIX; + $this->tableNameUser = $this->preparePrefixedTableName( + VersionedTableConstantsAlias::TABLE_NAME_USER, + $versionedDataStoreTablePrefix + ); + $this->tableNameUserVersion = $this->preparePrefixedTableName( + VersionedTableConstantsAlias::TABLE_NAME_USER_VERSION, + $versionedDataStoreTablePrefix + ); } - protected function preparePrefixedTableName(string $tableName): string + protected function preparePrefixedTableName(string $tableName, string $tablePrefixOverride = null): string { - return $this->connection->preparePrefixedTableName(TableConstants::TABLE_PREFIX . $tableName); + $tablePrefix = $tablePrefixOverride ?? TableConstants::TABLE_PREFIX; + return $this->connection->preparePrefixedTableName($tablePrefix . $tableName); } /** @@ -178,48 +194,48 @@ class Repository string $metadataHashSha256, DateTimeImmutable $updatedAt = null ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - $updatedAt = $updatedAt ?? new DateTimeImmutable(); + $updatedAt = $updatedAt ?? new DateTimeImmutable(); - $queryBuilder->update($this->tableNameIdp) - ->set( - TableConstants::TABLE_IDP_COLUMN_NAME_METADATA, - ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA - ) - ->set( - TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256, - ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 - ) - ->set( - TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT, - ':' . TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT - ) - ->setParameter( - TableConstants::TABLE_IDP_COLUMN_NAME_METADATA, - $metadata, - Types::STRING - ) - ->setParameter( - TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256, - $metadataHashSha256, - Types::STRING - ) - ->setParameter( - TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT, - $updatedAt, - Types::DATETIMETZ_IMMUTABLE - ) - ->where( - $queryBuilder->expr()->and( - $queryBuilder->expr()->eq( - TableConstants::TABLE_IDP_COLUMN_NAME_ID, - $queryBuilder->createNamedParameter($idpId, Types::INTEGER) - ) + $queryBuilder->update($this->tableNameIdp) + ->set( + TableConstants::TABLE_IDP_COLUMN_NAME_METADATA, + ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA ) - ); + ->set( + TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256, + ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 + ) + ->set( + TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT, + ':' . TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT + ) + ->setParameter( + TableConstants::TABLE_IDP_COLUMN_NAME_METADATA, + $metadata, + Types::STRING + ) + ->setParameter( + TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256, + $metadataHashSha256, + Types::STRING + ) + ->setParameter( + TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT, + $updatedAt, + Types::DATETIMETZ_IMMUTABLE + ) + ->where( + $queryBuilder->expr()->and( + $queryBuilder->expr()->eq( + TableConstants::TABLE_IDP_COLUMN_NAME_ID, + $queryBuilder->createNamedParameter($idpId, Types::INTEGER) + ) + ) + ); - try { $queryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf('Error executing query to update IdP. Error was: %s.', $exception->getMessage()); @@ -296,234 +312,52 @@ class Repository string $metadataHashSha256, DateTimeImmutable $updatedAt = null ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $updatedAt = $updatedAt ?? new DateTimeImmutable(); - - $queryBuilder->update($this->tableNameSp) - ->set( - TableConstants::TABLE_SP_COLUMN_NAME_METADATA, - ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA - ) - ->set( - TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256, - ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 - ) - ->set( - TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT, - ':' . TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT - ) - ->setParameter( - TableConstants::TABLE_SP_COLUMN_NAME_METADATA, - $metadata, - Types::STRING - ) - ->setParameter( - TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256, - $metadataHashSha256, - Types::STRING - ) - ->setParameter( - TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT, - $updatedAt, - Types::DATETIMETZ_IMMUTABLE - ) - ->where( - $queryBuilder->expr()->and( - $queryBuilder->expr()->eq( - TableConstants::TABLE_SP_COLUMN_NAME_ID, - $queryBuilder->createNamedParameter($spId, Types::INTEGER) - ) - ) - ); - - try { - $queryBuilder->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf('Error executing query to update SP. Error was: %s.', $exception->getMessage()); - $this->logger->error($message, compact('spId', 'metadata', 'metadataHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function getUser(string $identifierHashSha256): Result - { try { $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - /** @psalm-suppress TooManyArguments */ - $queryBuilder->select( - TableConstants::TABLE_USER_COLUMN_NAME_ID, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, - ) - ->from($this->tableNameUser) - ->where( - $queryBuilder->expr()->and( - $queryBuilder->expr()->eq( - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, - $queryBuilder->createNamedParameter($identifierHashSha256) - ) - ) - )->setMaxResults(1); - - return $queryBuilder->executeQuery(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to get user. Error was: %s.', - $identifierHashSha256, - $exception->getMessage() - ); - $this->logger->error($message, compact('identifierHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function insertUser( - string $identifier, - string $identifierHashSha256, - DateTimeImmutable $createdAt = null - ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + $updatedAt = $updatedAt ?? new DateTimeImmutable(); - $createdAt = $createdAt ?? new DateTimeImmutable(); - - $queryBuilder->insert($this->tableNameUser) - ->values( - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => $identifier, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => $identifierHashSha256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => $createdAt, - ], - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => Types::TEXT, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => Types::STRING, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE - ] - ); - - try { - $queryBuilder->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf('Error executing query to insert user. Error was: %s.', $exception->getMessage()); - $this->logger->error( - $message, - compact('identifier', 'identifierHashSha256') - ); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - - /** - * @throws StoreException - */ - public function getUserVersion(int $userId, string $attributesHashSha256): Result - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - /** @psalm-suppress TooManyArguments */ - $queryBuilder->select( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, - ) - ->from($this->tableNameUserVersion) + $queryBuilder->update($this->tableNameSp) + ->set( + TableConstants::TABLE_SP_COLUMN_NAME_METADATA, + ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA + ) + ->set( + TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256, + ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 + ) + ->set( + TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT, + ':' . TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT + ) + ->setParameter( + TableConstants::TABLE_SP_COLUMN_NAME_METADATA, + $metadata, + Types::STRING + ) + ->setParameter( + TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256, + $metadataHashSha256, + Types::STRING + ) + ->setParameter( + TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT, + $updatedAt, + Types::DATETIMETZ_IMMUTABLE + ) ->where( $queryBuilder->expr()->and( $queryBuilder->expr()->eq( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - $queryBuilder->createNamedParameter($userId, ParameterType::INTEGER) - ), - $queryBuilder->expr()->eq( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - $queryBuilder->createNamedParameter($attributesHashSha256) + TableConstants::TABLE_SP_COLUMN_NAME_ID, + $queryBuilder->createNamedParameter($spId, Types::INTEGER) ) ) - )->setMaxResults(1); - - return $queryBuilder->executeQuery(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to get user version for user ID %s and attribute array hash %s. Error was: %s.', - $userId, - $attributesHashSha256, - $exception->getMessage() - ); - $this->logger->error($message, compact('userId', 'attributesHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function insertUserVersion( - int $userId, - string $attributes, - string $attributesHashSha256, - DateTimeImmutable $createdAt = null - ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $createdAt = $createdAt ?? new DateTimeImmutable(); - - $queryBuilder->insert($this->tableNameUserVersion) - ->values( - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => $userId, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => $attributes, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => $attributesHashSha256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt, - ], - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => Types::BIGINT, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => Types::TEXT, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => Types::STRING, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, - ] - ); + ); - try { $queryBuilder->executeStatement(); } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to insert user version. Error was: %s.', - $exception->getMessage() - ); - $this->logger->error($message, compact('userId', 'attributesHashSha256')); + $message = sprintf('Error executing query to update SP. Error was: %s.', $exception->getMessage()); + $this->logger->error($message, compact('spId', 'metadata', 'metadataHashSha256')); throw new StoreException($message, (int)$exception->getCode(), $exception); } } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php old mode 100644 new mode 100755 index 40b45a00b61dc6f2ec2ea4d6119d7e01c6544d8e..34572f5854c6d8f3b63bc9640dc2cb792333bad0 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php @@ -31,22 +31,4 @@ class TableConstants extends BaseTableConstants public const TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 = 'metadata_hash_sha256'; public const TABLE_SP_COLUMN_NAME_CREATED_AT = 'created_at'; public const TABLE_SP_COLUMN_NAME_UPDATED_AT = 'updated_at'; - - // Table 'user' - public const TABLE_NAME_USER = 'user'; - public const TABLE_ALIAS_USER = self::TABLE_PREFIX . 'u'; - public const TABLE_USER_COLUMN_NAME_ID = 'id'; // int - public const TABLE_USER_COLUMN_NAME_IDENTIFIER = 'identifier'; // text, varies... (can be ePTID, which is long XML). - public const TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 = 'identifier_hash_sha256'; - public const TABLE_USER_COLUMN_NAME_CREATED_AT = 'created_at'; - - // Table 'user_version' (versioned attributes) - public const TABLE_NAME_USER_VERSION = 'user_version'; - public const TABLE_ALIAS_USER_VERSION = self::TABLE_PREFIX . 'uv'; - public const TABLE_ALIAS_USER_VERSION_2 = self::TABLE_ALIAS_USER_VERSION . '_2'; - public const TABLE_USER_VERSION_COLUMN_NAME_ID = 'id'; // int ID - public const TABLE_USER_VERSION_COLUMN_NAME_USER_ID = 'user_id'; // FK - public const TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES = 'attributes'; // Serialized attributes version - public const TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 = 'attributes_hash_sha256'; - public const TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT = 'created_at'; } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php old mode 100644 new mode 100755 index 6c3214b6565d5629f0d4a7d272159b63a325688e..411da5f7bf00bc54a1a0c200e5d041c888b905a5 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php @@ -11,13 +11,14 @@ use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface; use SimpleSAML\Module\accounting\Exceptions\StoreException; -use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; use Throwable; class Store extends AbstractStore implements StoreInterface { + use Store\UserVersionResolvingTrait; + protected HelpersManager $helpersManager; private Repository $repository; @@ -275,120 +276,6 @@ class Store extends AbstractStore implements StoreInterface } } - /** - * @throws StoreException - */ - public function resolveUserId(HashDecoratedState $hashDecoratedState): int - { - $userIdentifierAttributeName = $this->moduleConfiguration->getUserIdAttributeName(); - - $userIdentifierValue = $hashDecoratedState->getState()->getFirstAttributeValue($userIdentifierAttributeName); - if ($userIdentifierValue === null) { - $message = sprintf('Attributes do not contain user ID attribute %s.', $userIdentifierAttributeName); - throw new UnexpectedValueException($message); - } - - $userIdentifierValueHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifierValue); - - // Check if it already exists. - try { - $result = $this->repository->getUser($userIdentifierValueHashSha256); - $userId = $result->fetchOne(); - - if ($userId !== false) { - return (int)$userId; - } - } catch (Throwable $exception) { - $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - // Create new - try { - $this->repository->insertUser($userIdentifierValue, $userIdentifierValueHashSha256); - } catch (Throwable $exception) { - $message = sprintf( - 'Error inserting new user, however, continuing in case of race condition. Error was: %s.', - $exception->getMessage() - ); - $this->logger->warning($message); - } - - // Try again, this time it should exist... - try { - $result = $this->repository->getUser($userIdentifierValueHashSha256); - $userIdNew = $result->fetchOne(); - - if ($userIdNew !== false) { - return (int)$userIdNew; - } - - $message = sprintf( - 'Error fetching user even after insertion for identifier value hash SHA256 %s.', - $userIdentifierValueHashSha256 - ); - throw new StoreException($message); - } catch (Throwable $exception) { - $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int - { - $attributeArrayHashSha256 = $hashDecoratedState->getAttributesArrayHashSha256(); - - // Check if it already exists. - try { - $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); - $userVersionId = $result->fetchOne(); - - if ($userVersionId !== false) { - return (int)$userVersionId; - } - } catch (Throwable $exception) { - $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - // Create new - try { - $this->repository->insertUserVersion( - $userId, - serialize($hashDecoratedState->getState()->getAttributes()), - $attributeArrayHashSha256 - ); - } catch (Throwable $exception) { - $message = sprintf( - 'Error inserting new user version, however, continuing in case of race condition. Error was: %s.', - $exception->getMessage() - ); - $this->logger->warning($message); - } - - // Try again, this time it should exist... - try { - $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); - $userVersionIdNew = $result->fetchOne(); - - if ($userVersionIdNew !== false) { - return (int)$userVersionIdNew; - } - - $message = sprintf( - 'Error fetching user version even after insertion for user ID %s.', - $userId - ); - throw new StoreException($message); - } catch (Throwable $exception) { - $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - /** * @throws StoreException */ diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php old mode 100644 new mode 100755 index 7a37729835d79bc1dc1269406081ffd19215cf91..852ae2d3d6e07e10fd9cdc990314b299a43d4286 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -41,10 +41,10 @@ class CreateIdpTable extends AbstractMigration ->setAutoincrement(true); $table->addColumn('entity_id', Types::STRING) - ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH); + ->setLength(BaseTableConstantsAlias::COLUMN_ENTITY_ID_LENGTH); $table->addColumn('entity_id_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php old mode 100644 new mode 100755 index fdbd7c5240b20734e02756c8940db0ab2717d70d..261ab9091ac6c9fc1da18de9799530b0c058666c --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php @@ -6,11 +6,13 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; +use function sprintf; + class CreateIdpVersionTable extends AbstractMigration { protected function getLocalTablePrefix(): string @@ -43,7 +45,7 @@ class CreateIdpVersionTable extends AbstractMigration $table->addColumn('metadata', Types::TEXT); $table->addColumn('metadata_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); @@ -57,7 +59,7 @@ class CreateIdpVersionTable extends AbstractMigration $this->schemaManager->createTable($table); } catch (Throwable $exception) { throw $this->prepareGenericMigrationException( - \sprintf('Error creating table \'%s.', $tableName), + sprintf('Error creating table \'%s.', $tableName), $exception ); } @@ -74,7 +76,7 @@ class CreateIdpVersionTable extends AbstractMigration try { $this->schemaManager->dropTable($tableName); } catch (Throwable $exception) { - throw $this->prepareGenericMigrationException(\sprintf('Could not drop table %s.', $tableName), $exception); + throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception); } } } diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php old mode 100644 new mode 100755 index a168bdfc1f1dd0c3225cf92a7196f0f07504de3e..70e5ff92bcd9196428748ca321e46faaa1014ff3 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -41,10 +41,10 @@ class CreateSpTable extends AbstractMigration ->setAutoincrement(true); $table->addColumn('entity_id', Types::STRING) - ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH); + ->setLength(BaseTableConstantsAlias::COLUMN_ENTITY_ID_LENGTH); $table->addColumn('entity_id_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php old mode 100644 new mode 100755 index 6f7a4c097d3bcd08b5b46885c50af2cbf8fa4260..615759072aa253ca88418e23f4fec6c0d380aac2 --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -45,7 +45,7 @@ class CreateSpVersionTable extends AbstractMigration $table->addColumn('metadata', Types::TEXT); $table->addColumn('metadata_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php old mode 100644 new mode 100755 index 511bf5b71e3b3dddb0b7cafdb1964055d1fe638a..133b9f0d246c48120b85716c66c7fe34e8b4eddc --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -43,7 +43,7 @@ class CreateUserTable extends AbstractMigration ->setLength(65535); $table->addColumn('identifier_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php old mode 100644 new mode 100755 index 54b2693c37cdccdd75c9e33ae3c721b29c64ad7c..57c95d76e71f6c261f730289cb80e3d4bb958d3e --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php @@ -6,7 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Types; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstantsAlias; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use Throwable; @@ -46,7 +46,7 @@ class CreateUserVersionTable extends AbstractMigration ->setComment('Serialized attributes.'); $table->addColumn('attributes_hash_sha256', Types::STRING) - ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH) + ->setLength(BaseTableConstantsAlias::COLUMN_HASH_SHA265_HEXITS_LENGTH) ->setFixed(true); $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE); diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php old mode 100644 new mode 100755 index 0284c4e585714741f9582fbbf404e52d4501c0ab..b1a120bba880087b5172b78e5f5a1db88b0117dd --- a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php @@ -15,6 +15,8 @@ use Throwable; class Repository { + use Repository\UserVersionManagementTrait; + protected Connection $connection; protected LoggerInterface $logger; protected string $tableNameIdp; @@ -388,180 +390,6 @@ class Repository } } - /** - * @throws StoreException - */ - public function getUser(string $identifierHashSha256): Result - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - /** @psalm-suppress TooManyArguments */ - $queryBuilder->select( - TableConstants::TABLE_USER_COLUMN_NAME_ID, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, - ) - ->from($this->tableNameUser) - ->where( - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' . - $queryBuilder->createNamedParameter($identifierHashSha256) - )->setMaxResults(1); - - return $queryBuilder->executeQuery(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to get user by identifier hash SHA256 \'%s\'. Error was: %s.', - $identifierHashSha256, - $exception->getMessage() - ); - $this->logger->error($message, compact('identifierHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function insertUser( - string $identifier, - string $identifierHashSha256, - DateTimeImmutable $createdAt = null - ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $createdAt = $createdAt ?? new DateTimeImmutable(); - - $queryBuilder->insert($this->tableNameUser) - ->values( - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => $identifier, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => $identifierHashSha256, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => $createdAt, - ], - [ - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => Types::TEXT, - TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => Types::STRING, - TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE - ] - ); - - try { - $queryBuilder->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf('Error executing query to insert user. Error was: %s.', $exception->getMessage()); - $this->logger->error($message, compact('identifier', 'identifierHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function getUserVersion(int $userId, string $attributesHashSha256): Result - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - /** @psalm-suppress TooManyArguments */ - $queryBuilder->select( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, - ) - ->from($this->tableNameUserVersion) - ->where( - $queryBuilder->expr()->and( - $queryBuilder->expr()->eq( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - $queryBuilder->createNamedParameter($userId, ParameterType::INTEGER) - ), - $queryBuilder->expr()->eq( - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - $queryBuilder->createNamedParameter($attributesHashSha256) - ) - ) - )->setMaxResults(1); - - return $queryBuilder->executeQuery(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to get user version for user ID %s and attribute array hash %s. Error was: %s.', - $userId, - $attributesHashSha256, - $exception->getMessage() - ); - $this->logger->error($message, compact('userId', 'attributesHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - - /** - * @throws StoreException - */ - public function insertUserVersion( - int $userId, - string $attributes, - string $attributesHashSha256, - DateTimeImmutable $createdAt = null - ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $createdAt = $createdAt ?? new DateTimeImmutable(); - - $queryBuilder->insert($this->tableNameUserVersion) - ->values( - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => $userId, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => $attributes, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => $attributesHashSha256, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt, - ], - [ - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => Types::BIGINT, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => Types::TEXT, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => Types::STRING, - TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, - ] - ); - - try { - $queryBuilder->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to insert user version. Error was: %s.', - $exception->getMessage() - ); - $this->logger->error($message, compact('userId', 'attributesHashSha256')); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } - /** * @throws StoreException */ @@ -620,39 +448,39 @@ class Repository int $userVersionId, DateTimeImmutable $createdAt = null ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - $createdAt = $createdAt ?? new DateTimeImmutable(); + $createdAt = $createdAt ?? new DateTimeImmutable(); - $queryBuilder->insert($this->tableNameIdpSpUserVersion) - ->values( - [ - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => ':' . - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => ':' . - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => ':' . - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => $idpVersionId, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => $spVersionId, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => $userVersionId, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt, - ], - [ - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => Types::BIGINT, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => Types::BIGINT, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT, - TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE - ] - ); + $queryBuilder->insert($this->tableNameIdpSpUserVersion) + ->values( + [ + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => ':' . + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => ':' . + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => ':' . + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => $idpVersionId, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => $spVersionId, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => $userVersionId, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt, + ], + [ + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE + ] + ); - try { $queryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf( diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository/UserVersionManagementTrait.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository/UserVersionManagementTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..5fa5e6687322ee155c7ef0e2f74d3793e01f9bee --- /dev/null +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository/UserVersionManagementTrait.php @@ -0,0 +1,188 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository; + +use DateTimeImmutable; +use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Result; +use Doctrine\DBAL\Types\Types; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; + +trait UserVersionManagementTrait +{ + /** + * @throws StoreException + */ + public function getUser(string $identifierHashSha256): Result + { + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + /** @psalm-suppress TooManyArguments */ + $queryBuilder->select( + TableConstants::TABLE_USER_COLUMN_NAME_ID, + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, + TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, + ) + ->from($this->tableNameUser) + ->where( + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' . + $queryBuilder->createNamedParameter($identifierHashSha256) + )->setMaxResults(1); + + return $queryBuilder->executeQuery(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to get user by identifier hash SHA256 \'%s\'. Error was: %s.', + $identifierHashSha256, + $exception->getMessage() + ); + $this->logger->error($message, compact('identifierHashSha256')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } + + /** + * @throws StoreException + */ + public function insertUser( + string $identifier, + string $identifierHashSha256, + DateTimeImmutable $createdAt = null + ): void { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $createdAt = $createdAt ?? new DateTimeImmutable(); + + $queryBuilder->insert($this->tableNameUser) + ->values( + [ + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => ':' . + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER, + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => ':' . + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256, + TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => $identifier, + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => $identifierHashSha256, + TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => $createdAt, + ], + [ + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => Types::TEXT, + TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => Types::STRING, + TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE + ] + ); + + try { + $queryBuilder->executeStatement(); + } catch (Throwable $exception) { + $message = sprintf('Error executing query to insert user. Error was: %s.', $exception->getMessage()); + $this->logger->error($message, compact('identifier', 'identifierHashSha256')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } + + /** + * @throws StoreException + */ + public function getUserVersion(int $userId, string $attributesHashSha256): Result + { + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + /** @psalm-suppress TooManyArguments */ + $queryBuilder->select( + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, + ) + ->from($this->tableNameUserVersion) + ->where( + $queryBuilder->expr()->and( + $queryBuilder->expr()->eq( + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, + $queryBuilder->createNamedParameter($userId, ParameterType::INTEGER) + ), + $queryBuilder->expr()->eq( + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, + $queryBuilder->createNamedParameter($attributesHashSha256) + ) + ) + )->setMaxResults(1); + + return $queryBuilder->executeQuery(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to get user version for user ID %s and attribute array hash %s. Error was: %s.', + $userId, + $attributesHashSha256, + $exception->getMessage() + ); + $this->logger->error($message, compact('userId', 'attributesHashSha256')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } + + /** + * @throws StoreException + */ + public function insertUserVersion( + int $userId, + string $attributes, + string $attributesHashSha256, + DateTimeImmutable $createdAt = null + ): void { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $createdAt = $createdAt ?? new DateTimeImmutable(); + + $queryBuilder->insert($this->tableNameUserVersion) + ->values( + [ + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => ':' . + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => ':' . + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => ':' . + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => $userId, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => $attributes, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => $attributesHashSha256, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt, + ], + [ + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => Types::BIGINT, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => Types::TEXT, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => Types::STRING, + TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, + ] + ); + + try { + $queryBuilder->executeStatement(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to insert user version. Error was: %s.', + $exception->getMessage() + ); + $this->logger->error($message, compact('userId', 'attributesHashSha256')); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/TableConstants.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/UserVersionResolvingTrait.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/UserVersionResolvingTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..60ec4b2e120723514c0af209a9465db6183c34c4 --- /dev/null +++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/UserVersionResolvingTrait.php @@ -0,0 +1,125 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; +use Throwable; + +trait UserVersionResolvingTrait +{ + /** + * @throws StoreException + */ + public function resolveUserId(HashDecoratedState $hashDecoratedState): int + { + $userIdentifierAttributeName = $this->moduleConfiguration->getUserIdAttributeName(); + + $userIdentifierValue = $hashDecoratedState->getState()->getFirstAttributeValue($userIdentifierAttributeName); + if ($userIdentifierValue === null) { + $message = sprintf('Attributes do not contain user ID attribute %s.', $userIdentifierAttributeName); + throw new UnexpectedValueException($message); + } + + $userIdentifierValueHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifierValue); + + // Check if it already exists. + try { + $result = $this->repository->getUser($userIdentifierValueHashSha256); + $userId = $result->fetchOne(); + + if ($userId !== false) { + return (int)$userId; + } + } catch (Throwable $exception) { + $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + + // Create new + try { + $this->repository->insertUser($userIdentifierValue, $userIdentifierValueHashSha256); + } catch (Throwable $exception) { + $message = sprintf( + 'Error inserting new user, however, continuing in case of race condition. Error was: %s.', + $exception->getMessage() + ); + $this->logger->warning($message); + } + + // Try again, this time it should exist... + try { + $result = $this->repository->getUser($userIdentifierValueHashSha256); + $userIdNew = $result->fetchOne(); + + if ($userIdNew !== false) { + return (int)$userIdNew; + } + + $message = sprintf( + 'Error fetching user even after insertion for identifier value hash SHA256 %s.', + $userIdentifierValueHashSha256 + ); + throw new StoreException($message); + } catch (Throwable $exception) { + $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage()); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } + + /** + * @throws StoreException + */ + public function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int + { + $attributeArrayHashSha256 = $hashDecoratedState->getAttributesArrayHashSha256(); + + // Check if it already exists. + try { + $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); + $userVersionId = $result->fetchOne(); + + if ($userVersionId !== false) { + return (int)$userVersionId; + } + } catch (Throwable $exception) { + $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + + // Create new + try { + $this->repository->insertUserVersion( + $userId, + serialize($hashDecoratedState->getState()->getAttributes()), + $attributeArrayHashSha256 + ); + } catch (Throwable $exception) { + $message = sprintf( + 'Error inserting new user version, however, continuing in case of race condition. Error was: %s.', + $exception->getMessage() + ); + $this->logger->warning($message); + } + + // Try again, this time it should exist... + try { + $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256); + $userVersionIdNew = $result->fetchOne(); + + if ($userVersionIdNew !== false) { + return (int)$userVersionIdNew; + } + + $message = sprintf( + 'Error fetching user version even after insertion for user ID %s.', + $userId + ); + throw new StoreException($message); + } catch (Throwable $exception) { + $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage()); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/Bases/HashDecoratedState.php b/src/Data/Stores/Accounting/Bases/HashDecoratedState.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/Bases/TableConstants.php b/src/Data/Stores/Accounting/Bases/TableConstants.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php old mode 100644 new mode 100755 index 8e84d9cd328ef6c07bee3931c53064d738eb49bf..69d1c716948d19205a5e0f2e865a1475b015c845 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php @@ -5,23 +5,24 @@ declare(strict_types=1); namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current; use DateTimeImmutable; +use Doctrine\DBAL\Exception; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store as BaseStore; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Store\GettableConnectedServicesTrait; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ConnectedServicesInterface; use SimpleSAML\Module\accounting\Entities\Authentication\Event; -use SimpleSAML\Module\accounting\Entities\ConnectedService; -use SimpleSAML\Module\accounting\Entities\User; use SimpleSAML\Module\accounting\Exceptions\StoreException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; -use Throwable; class Store extends BaseStore implements ConnectedServicesInterface { + use GettableConnectedServicesTrait; + protected Repository $repository; /** @@ -67,7 +68,7 @@ class Store extends BaseStore implements ConnectedServicesInterface } /** - * @throws StoreException + * @throws StoreException|Exception */ public function persist(Event $authenticationEvent): void { @@ -96,54 +97,6 @@ class Store extends BaseStore implements ConnectedServicesInterface } } - /** - * @throws StoreException - */ - public function getConnectedServices(string $userIdentifier): ConnectedService\Bag - { - $connectedServiceProviderBag = new ConnectedService\Bag(); - - $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier); - - $results = $this->repository->getConnectedServices($userIdentifierHashSha256); - - if (empty($results)) { - return $connectedServiceProviderBag; - } - - try { - $databasePlatform = $this->connection->dbal()->getDatabasePlatform(); - - /** @var array $result */ - foreach ($results as $result) { - $rawConnectedServiceProvider = new RawConnectedService($result, $databasePlatform); - - $serviceProvider = $this->helpersManager - ->getProviderResolver() - ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata()); - $user = new User($rawConnectedServiceProvider->getUserAttributes()); - - $connectedServiceProviderBag->addOrReplace( - new ConnectedService( - $serviceProvider, - $rawConnectedServiceProvider->getNumberOfAuthentications(), - $rawConnectedServiceProvider->getLastAuthenticationAt(), - $rawConnectedServiceProvider->getFirstAuthenticationAt(), - $user - ) - ); - } - } catch (Throwable $exception) { - $message = sprintf( - 'Error populating connected service provider bag. Error was: %s', - $exception->getMessage() - ); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - return $connectedServiceProviderBag; - } - /** * @throws StoreException */ diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php old mode 100644 new mode 100755 index bd61e6d0cf4172fe63f397f7e314d5887941c211..fa770ade3b25899cec733cf38ddcfc8f36b6f0b8 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php @@ -61,14 +61,17 @@ class Version20240505400CreateConnectedServiceTable extends AbstractMigration ['id'] ); + // We are using versioned data for user management. + $versionedDataStoreTablePrefix = 'vds_'; + $table->addForeignKeyConstraint( - $this->preparePrefixedTableName('user'), + $this->preparePrefixedTableName('user', $versionedDataStoreTablePrefix), ['user_id'], ['id'] ); $table->addForeignKeyConstraint( - $this->preparePrefixedTableName('user_version'), + $this->preparePrefixedTableName('user_version', $versionedDataStoreTablePrefix), ['user_version_id'], ['id'] ); diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php old mode 100644 new mode 100755 index 74a8e6264d64dd2f33bbe01513e1315b9acf8120..f36e0a76efe5c9bbc8a239f9d296e9ef83bab356 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php @@ -10,14 +10,21 @@ use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Types; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository as BaseRepository; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants - as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedBaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Repository\DeletableConnectedServicesTrait; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Exceptions\StoreException; use Throwable; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; class Repository extends BaseRepository { + use DeletableConnectedServicesTrait; + protected string $tableNameConnectedService; public function __construct(Connection $connection, LoggerInterface $logger) @@ -86,62 +93,63 @@ class Repository extends BaseRepository int $count = 1, DateTimeImmutable $createdUpdatedAt = null ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable(); - $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt; - $count = max($count, 1); - $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable(); + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - $queryBuilder->insert($this->tableNameConnectedService) - ->values( - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => $spId, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => $userId, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => $userVersionId, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => - $firstAuthenticationAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => - $lastAuthenticationAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt, - ], - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => - Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => - Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE, - ] - ); + $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable(); + $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt; + $count = max($count, 1); + $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable(); + + $queryBuilder->insert($this->tableNameConnectedService) + ->values( + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => $spId, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => $userId, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => $userVersionId, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => + $firstAuthenticationAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => + $lastAuthenticationAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt, + ], + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => + Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => + Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE, + ] + ); - try { $queryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf( @@ -167,50 +175,50 @@ class Repository extends BaseRepository ): void { $incrementCountBy = max($incrementCountBy, 1); - $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + try { + $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); - $updateCountQueryBuilder->update($this->tableNameConnectedService) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy - ) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, - ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID - ) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT - ) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, - ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT - ) - ->setParameter( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, - $userVersionId, - Types::INTEGER - ) - ->setParameter( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - $happenedAt, - Types::DATETIMETZ_IMMUTABLE - ) - ->setParameter( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, - new DateTimeImmutable(), - Types::DATETIMETZ_IMMUTABLE - ) - ->where( - $updateCountQueryBuilder->expr()->and( - $updateCountQueryBuilder->expr()->eq( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID, - $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER) - ) + $updateCountQueryBuilder->update($this->tableNameConnectedService) + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy ) - ); + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, + ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID + ) + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT + ) + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, + ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT + ) + ->setParameter( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID, + $userVersionId, + Types::INTEGER + ) + ->setParameter( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + $happenedAt, + Types::DATETIMETZ_IMMUTABLE + ) + ->setParameter( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, + new DateTimeImmutable(), + Types::DATETIMETZ_IMMUTABLE + ) + ->where( + $updateCountQueryBuilder->expr()->and( + $updateCountQueryBuilder->expr()->eq( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID, + $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER) + ) + ) + ); - try { $updateCountQueryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf( @@ -237,22 +245,22 @@ class Repository extends BaseRepository $connectedServicesQueryBuilder->select( BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_METADATA . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, - BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . - BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, + VersionedBaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . + VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' . + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES, )->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE) ->innerJoin( //'ccs', @@ -270,27 +278,27 @@ class Repository extends BaseRepository ->innerJoin( TableConstants::TABLE_ALIAS_CONNECTED_SERVICE, $this->tableNameUser, - BaseTableConstants::TABLE_ALIAS_USER, + VersionedBaseTableConstants::TABLE_ALIAS_USER, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_USER . '.' . - BaseTableConstants::TABLE_USER_COLUMN_NAME_ID + VersionedBaseTableConstants::TABLE_ALIAS_USER . '.' . + VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_ID ) ->innerJoin( TableConstants::TABLE_ALIAS_CONNECTED_SERVICE, $this->tableNameUserVersion, - BaseTableConstants::TABLE_ALIAS_USER_VERSION, + VersionedBaseTableConstants::TABLE_ALIAS_USER_VERSION, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . - BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID + VersionedBaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . + VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID ) ->where( - BaseTableConstants::TABLE_ALIAS_USER . '.' . - BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' . + VersionedBaseTableConstants::TABLE_ALIAS_USER . '.' . + VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' . $connectedServicesQueryBuilder->createNamedParameter($userIdentifierHashSha256) )->orderBy( - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, 'DESC' ); @@ -304,29 +312,4 @@ class Repository extends BaseRepository throw new StoreException($message, (int)$exception->getCode(), $exception); } } - - /** - * @throws StoreException - */ - public function deleteConnectedServicesOlderThan(DateTimeImmutable $dateTime): void - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $queryBuilder->delete($this->tableNameConnectedService) - ->where( - $queryBuilder->expr()->lt( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE) - ) - )->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to delete old connected services. Error was: %s.', - $exception->getMessage() - ); - $this->logger->error($message); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } } diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php old mode 100644 new mode 100755 index d0331be7b040900eca9b549e26e09bdc11bec9e0..f0a9dc6f131762dfb90cb1f648e7752d3d4a77be --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php @@ -13,7 +13,6 @@ class TableConstants public const TABLE_NAME_CONNECTED_SERVICE = 'connected_service'; public const TABLE_ALIAS_CONNECTED_SERVICE = BaseTableConstants::TABLE_PREFIX . 'cs'; public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID = 'id'; // int - public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_ID = 'idp_id'; // int public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID = 'sp_id'; // int public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID = 'user_id'; // int public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID = 'user_version_id'; // int @@ -22,12 +21,4 @@ class TableConstants public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT = 'count'; // int public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT = 'created_at'; // datetime public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT = 'updated_at'; // datetime - - // Entity 'ConnectedService' (service provider) related. - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA = 'sp_metadata'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes'; } diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/EntityTableConstants.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/EntityTableConstants.php new file mode 100644 index 0000000000000000000000000000000000000000..3f255fa5c72bc0683436a237cac8e6c1a5112aac --- /dev/null +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/EntityTableConstants.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal; + +class EntityTableConstants +{ + // Entity 'ConnectedService' (service provider) related. + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id'; + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications'; + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at'; + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at'; + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA = 'sp_metadata'; + public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes'; +} diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php old mode 100644 new mode 100755 index 41334eaf9d90deca5f43f7269cc332c8794ba3b2..65cb1791831fc232c2b0d4f0469414d6ebeeb080 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php @@ -6,7 +6,6 @@ namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\ use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants; use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity; use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; @@ -23,23 +22,23 @@ class RawConnectedService extends AbstractRawEntity parent::__construct($rawRow, $abstractPlatform); $this->numberOfAuthentications = (int)$rawRow[ - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS ]; $this->lastAuthenticationAt = $this->resolveDateTimeImmutable( - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT] + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT] ); $this->firstAuthenticationAt = $this->resolveDateTimeImmutable( - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT] + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT] ); $this->serviceProviderMetadata = $this->resolveServiceProviderMetadata( - (string)$rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + (string)$rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] ); $this->userAttributes = $this->resolveUserAttributes( - (string)$rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + (string)$rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); } @@ -89,11 +88,11 @@ class RawConnectedService extends AbstractRawEntity protected function validate(array $rawRow): void { $columnsToCheck = [ - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES, ]; foreach ($columnsToCheck as $column) { @@ -103,44 +102,44 @@ class RawConnectedService extends AbstractRawEntity } if ( - ! is_numeric($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]) + ! is_numeric($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]) ) { $message = sprintf( 'Column %s must be numeric.', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS ); throw new UnexpectedValueException($message); } /** @noinspection DuplicatedCode */ - if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT ); throw new UnexpectedValueException($message); } - if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT ); throw new UnexpectedValueException($message); } - if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA ); throw new UnexpectedValueException($message); } - if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES])) { + if (! is_string($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES])) { $message = sprintf( 'Column %s must be string.', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES ); throw new UnexpectedValueException($message); } diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Repository/DeletableConnectedServicesTrait.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Repository/DeletableConnectedServicesTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..2d795028aa1d9a4e3a607a5605ffcba1ada7b832 --- /dev/null +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Repository/DeletableConnectedServicesTrait.php @@ -0,0 +1,37 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Repository; + +use DateTimeImmutable; +use Doctrine\DBAL\Types\Types; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; + +trait DeletableConnectedServicesTrait +{ + /** + * @throws StoreException + */ + public function deleteConnectedServicesOlderThan(DateTimeImmutable $dateTime): void + { + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $queryBuilder->delete($this->tableNameConnectedService) + ->where( + $queryBuilder->expr()->lt( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE) + ) + )->executeStatement(); + } catch (Throwable $exception) { + $message = sprintf( + 'Error executing query to delete old connected services. Error was: %s.', + $exception->getMessage() + ); + $this->logger->error($message); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + } +} diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Store/GettableConnectedServicesTrait.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Store/GettableConnectedServicesTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..fa22c8a189c001e29499441ca148860e29653daf --- /dev/null +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Traits/Store/GettableConnectedServicesTrait.php @@ -0,0 +1,60 @@ +<?php + +namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Store; + +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService; +use SimpleSAML\Module\accounting\Entities\ConnectedService; +use SimpleSAML\Module\accounting\Entities\User; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use Throwable; + +trait GettableConnectedServicesTrait +{ + /** + * @throws StoreException + */ + public function getConnectedServices(string $userIdentifier): ConnectedService\Bag + { + $connectedServiceProviderBag = new ConnectedService\Bag(); + + $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier); + + $results = $this->repository->getConnectedServices($userIdentifierHashSha256); + + if (empty($results)) { + return $connectedServiceProviderBag; + } + + try { + $databasePlatform = $this->connection->dbal()->getDatabasePlatform(); + + /** @var array $result */ + foreach ($results as $result) { + $rawConnectedServiceProvider = new RawConnectedService($result, $databasePlatform); + + $serviceProvider = $this->helpersManager + ->getProviderResolver() + ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata()); + $user = new User($rawConnectedServiceProvider->getUserAttributes()); + + $connectedServiceProviderBag->addOrReplace( + new ConnectedService( + $serviceProvider, + $rawConnectedServiceProvider->getNumberOfAuthentications(), + $rawConnectedServiceProvider->getLastAuthenticationAt(), + $rawConnectedServiceProvider->getFirstAuthenticationAt(), + $user + ) + ); + } + } catch (Throwable $exception) { + $message = sprintf( + 'Error populating connected service provider bag. Error was: %s', + $exception->getMessage() + ); + throw new StoreException($message, (int)$exception->getCode(), $exception); + } + + return $connectedServiceProviderBag; + } +} diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php old mode 100644 new mode 100755 index 6887a5bff291b1b36398e09e0cb633b9a6c94de7..6f81fdec9771f670bfe27c9f3f836db4db71ae52 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php @@ -5,23 +5,24 @@ declare(strict_types=1); namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned; use DateTimeImmutable; +use Doctrine\DBAL\Exception; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store as BaseStore; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Store\GettableConnectedServicesTrait; use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ConnectedServicesInterface; use SimpleSAML\Module\accounting\Entities\Authentication\Event; -use SimpleSAML\Module\accounting\Entities\ConnectedService; -use SimpleSAML\Module\accounting\Entities\User; use SimpleSAML\Module\accounting\Exceptions\StoreException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; -use Throwable; class Store extends BaseStore implements ConnectedServicesInterface { + use GettableConnectedServicesTrait; + protected Repository $repository; /** @@ -67,7 +68,7 @@ class Store extends BaseStore implements ConnectedServicesInterface } /** - * @throws StoreException + * @throws StoreException|Exception */ public function persist(Event $authenticationEvent): void { @@ -96,54 +97,6 @@ class Store extends BaseStore implements ConnectedServicesInterface $this->repository->touchConnectedServiceVersionsTimestamp($userId, $spId); } - /** - * @throws StoreException - */ - public function getConnectedServices(string $userIdentifier): ConnectedService\Bag - { - $connectedServiceProviderBag = new ConnectedService\Bag(); - - $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier); - - $results = $this->repository->getConnectedServices($userIdentifierHashSha256); - - if (empty($results)) { - return $connectedServiceProviderBag; - } - - try { - $databasePlatform = $this->connection->dbal()->getDatabasePlatform(); - - /** @var array $result */ - foreach ($results as $result) { - $rawConnectedServiceProvider = new RawConnectedService($result, $databasePlatform); - - $serviceProvider = $this->helpersManager - ->getProviderResolver() - ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata()); - $user = new User($rawConnectedServiceProvider->getUserAttributes()); - - $connectedServiceProviderBag->addOrReplace( - new ConnectedService( - $serviceProvider, - $rawConnectedServiceProvider->getNumberOfAuthentications(), - $rawConnectedServiceProvider->getLastAuthenticationAt(), - $rawConnectedServiceProvider->getFirstAuthenticationAt(), - $user - ) - ); - } - } catch (Throwable $exception) { - $message = sprintf( - 'Error populating connected service provider bag. Error was: %s', - $exception->getMessage() - ); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - - return $connectedServiceProviderBag; - } - /** * @throws StoreException */ diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php old mode 100644 new mode 100755 index 51f2f5a849382ce83cd96284b461e06bc1a098eb..bba95b63da07e3c326997a9b20556f62318445be --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php @@ -10,14 +10,19 @@ use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Types; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository as BaseRepository; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants - as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Traits\Repository\DeletableConnectedServicesTrait; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Exceptions\StoreException; use Throwable; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; class Repository extends BaseRepository { + use DeletableConnectedServicesTrait; + protected string $tableNameConnectedService; public function __construct(Connection $connection, LoggerInterface $logger) @@ -78,54 +83,55 @@ class Repository extends BaseRepository int $count = 1, DateTimeImmutable $createdUpdatedAt = null ): void { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); + try { + $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable(); - $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt; - $count = max($count, 1); - $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable(); + $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable(); + $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt; + $count = max($count, 1); + $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable(); - $queryBuilder->insert($this->tableNameConnectedService) - ->values( - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, - ] - ) - ->setParameters( - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => $idpSpUserVersionId, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => - $firstAuthenticationAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => - $lastAuthenticationAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt, - ], - [ - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => - Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => - Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE, - ] - ); + $queryBuilder->insert($this->tableNameConnectedService) + ->values( + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, + ] + ) + ->setParameters( + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => + $idpSpUserVersionId, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => + $firstAuthenticationAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => + $lastAuthenticationAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt, + ], + [ + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => + Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => + Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE, + ] + ); - try { $queryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf( @@ -148,34 +154,34 @@ class Repository extends BaseRepository DateTimeImmutable $happenedAt, int $incrementCountBy = 1 ): void { - $incrementCountBy = max($incrementCountBy, 1); + try { + $incrementCountBy = max($incrementCountBy, 1); - $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); - $updateCountQueryBuilder->update($this->tableNameConnectedService) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy - ) - ->set( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT - ) - ->setParameter( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, - $happenedAt, - Types::DATETIMETZ_IMMUTABLE - ) - ->where( - $updateCountQueryBuilder->expr()->and( - $updateCountQueryBuilder->expr()->eq( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID, - $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER) - ) + $updateCountQueryBuilder->update($this->tableNameConnectedService) + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT, + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy ) - ); + ->set( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT + ) + ->setParameter( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + $happenedAt, + Types::DATETIMETZ_IMMUTABLE + ) + ->where( + $updateCountQueryBuilder->expr()->and( + $updateCountQueryBuilder->expr()->eq( + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID, + $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER) + ) + ) + ); - try { $updateCountQueryBuilder->executeStatement(); } catch (Throwable $exception) { $message = sprintf( @@ -198,92 +204,92 @@ class Repository extends BaseRepository int $spId, DateTimeImmutable $happenedAt = null ): void { - $happenedAt = $happenedAt ?? new DateTimeImmutable(); + try { + $happenedAt = $happenedAt ?? new DateTimeImmutable(); - $selectConnectedServiceVersionsQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $selectConnectedServiceVersionsQueryBuilder = $this->connection->dbal()->createQueryBuilder(); - $selectConnectedServiceVersionsQueryBuilder->select( - TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '. ' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID - ) - ->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE) - ->innerJoin( - //'vcs', - TableConstants::TABLE_ALIAS_CONNECTED_SERVICE, - //'vds_idp_sp_user_version', - $this->tableNameIdpSpUserVersion, + $selectConnectedServiceVersionsQueryBuilder->select( + TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '. ' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID + ) + ->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE) + ->innerJoin( + //'vcs', + TableConstants::TABLE_ALIAS_CONNECTED_SERVICE, + //'vds_idp_sp_user_version', + $this->tableNameIdpSpUserVersion, + //'visuv', + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, + //'vcs.idp_sp_user_version_id = visuv.id' + TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . + TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' . + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . + BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID + )->innerJoin( //'visuv', - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, - //'vcs.idp_sp_user_version_id = visuv.id' - TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . - BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID - )->innerJoin( - //'visuv', - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, - //'vds_sp_version', - $this->tableNameSpVersion, + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, + //'vds_sp_version', + $this->tableNameSpVersion, + //'vsv', + BaseTableConstants::TABLE_ALIAS_SP_VERSION, + //'visuv.sp_version_id = vsv.id' + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . + BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' . + BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . + BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID + )->innerJoin( //'vsv', - BaseTableConstants::TABLE_ALIAS_SP_VERSION, - //'visuv.sp_version_id = vsv.id' - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . - BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . - BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID - )->innerJoin( - //'vsv', - BaseTableConstants::TABLE_ALIAS_SP_VERSION, - //'vds_sp', - $this->tableNameSp, - //'vs', - BaseTableConstants::TABLE_ALIAS_SP, - //'vsv.sp_id = vs.id' - BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . - BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID . - ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' . - BaseTableConstants::TABLE_SP_COLUMN_NAME_ID - )->innerJoin( - //'visuv', - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, - //'vds_user_version', - $this->tableNameUserVersion, + BaseTableConstants::TABLE_ALIAS_SP_VERSION, + //'vds_sp', + $this->tableNameSp, + //'vs', + BaseTableConstants::TABLE_ALIAS_SP, + //'vsv.sp_id = vs.id' + BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . + BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID . + ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' . + BaseTableConstants::TABLE_SP_COLUMN_NAME_ID + )->innerJoin( + //'visuv', + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION, + //'vds_user_version', + $this->tableNameUserVersion, + //'vuv', + BaseTableConstants::TABLE_ALIAS_USER_VERSION, + //'visuv.user_version_id = vuv.id' + BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . + BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' . + BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . + BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID + )->innerJoin( //'vuv', - BaseTableConstants::TABLE_ALIAS_USER_VERSION, - //'visuv.user_version_id = vuv.id' - BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' . - BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . - BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID - )->innerJoin( - //'vuv', - BaseTableConstants::TABLE_ALIAS_USER_VERSION, - //'vds_user', - $this->tableNameUser, - //'vu', - BaseTableConstants::TABLE_ALIAS_USER, - //'vuv.user_id = vu.id' - BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . - BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' . - BaseTableConstants::TABLE_ALIAS_USER . '.' . - BaseTableConstants::TABLE_USER_COLUMN_NAME_ID - ) - ->where( - $selectConnectedServiceVersionsQueryBuilder->expr()->and( - $selectConnectedServiceVersionsQueryBuilder->expr()->eq( - //'vs.id = ' . - BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ID, - $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($spId) - ), - $selectConnectedServiceVersionsQueryBuilder->expr()->eq( - //'vu.id = ' . - BaseTableConstants::TABLE_ALIAS_USER . '.' . BaseTableConstants::TABLE_USER_COLUMN_NAME_ID, - $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($userId) - ) + BaseTableConstants::TABLE_ALIAS_USER_VERSION, + //'vds_user', + $this->tableNameUser, + //'vu', + BaseTableConstants::TABLE_ALIAS_USER, + //'vuv.user_id = vu.id' + BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . + BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' . + BaseTableConstants::TABLE_ALIAS_USER . '.' . + BaseTableConstants::TABLE_USER_COLUMN_NAME_ID ) - ); + ->where( + $selectConnectedServiceVersionsQueryBuilder->expr()->and( + $selectConnectedServiceVersionsQueryBuilder->expr()->eq( + //'vs.id = ' . + BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ID, + $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($spId) + ), + $selectConnectedServiceVersionsQueryBuilder->expr()->eq( + //'vu.id = ' . + BaseTableConstants::TABLE_ALIAS_USER . '.' . BaseTableConstants::TABLE_USER_COLUMN_NAME_ID, + $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($userId) + ) + ) + ); - try { /** @var array<array-key,string> $connectedServiceVersions */ $connectedServiceVersions = $selectConnectedServiceVersionsQueryBuilder->executeQuery()->fetchFirstColumn(); } catch (Throwable $exception) { @@ -319,6 +325,7 @@ class Repository extends BaseRepository try { $updateLastAuthenticationAtQueryBuilder->executeStatement(); + // @codeCoverageIgnoreStart } catch (Throwable $exception) { $message = sprintf( 'Error touching connected service versions. Error was: %s.', @@ -330,6 +337,7 @@ class Repository extends BaseRepository ); throw new StoreException($message, (int)$exception->getCode(), $exception); } + // @codeCoverageIgnoreEnd } /** @@ -346,19 +354,19 @@ class Repository extends BaseRepository //'vs.entity_id AS sp_entity_id', BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, //'SUM(vcs.count) AS number_of_authentications', 'SUM(' . TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ') AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, //'MAX(vcs.last_authentication_at) AS last_authentication_at', 'MAX(' . TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT . ') AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT, //'MIN(vcs.first_authentication_at) AS first_authentication_at', 'MIN(' . TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT . ') AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT, )->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE) ->innerJoin( //'vcs', @@ -428,10 +436,10 @@ class Repository extends BaseRepository $connectedServicesQueryBuilder->createNamedParameter($userIdentifierHashSha256) )->groupBy( //'sp_entity_id' - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID )->orderBy( //'number_of_authentications', - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS, 'DESC' ); @@ -440,15 +448,15 @@ class Repository extends BaseRepository //'vs.entity_id AS sp_entity_id', BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID, //'vsv.metadata AS sp_metadata', BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' . BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA, //'vuv.attributes AS user_attributes', BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' . BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' . - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES )->from( //'vds_connected_service', $this->tableNameConnectedService, @@ -584,29 +592,4 @@ class Repository extends BaseRepository throw new StoreException($message, (int)$exception->getCode(), $exception); } } - - /** - * @throws StoreException - */ - public function deleteConnectedServicesOlderThan(DateTimeImmutable $dateTime): void - { - try { - $queryBuilder = $this->connection->dbal()->createQueryBuilder(); - - $queryBuilder->delete($this->tableNameConnectedService) - ->where( - $queryBuilder->expr()->lt( - TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT, - $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE) - ) - )->executeStatement(); - } catch (Throwable $exception) { - $message = sprintf( - 'Error executing query to delete old connected services. Error was: %s.', - $exception->getMessage() - ); - $this->logger->error($message); - throw new StoreException($message, (int)$exception->getCode(), $exception); - } - } } diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php old mode 100644 new mode 100755 index 028c4d501dc256f2363b09af35a46891b2ba7cf2..19f941117d2855e56bcfd3898322f1ceeba43b31 --- a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php +++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php @@ -19,12 +19,4 @@ class TableConstants public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT = 'count'; // int public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT = 'created_at'; // datetime public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT = 'updated_at'; // datetime - - // Entity 'ConnectedService' (service provider) related. - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA = 'sp_metadata'; - public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes'; } diff --git a/src/Data/Stores/Bases/AbstractStore.php b/src/Data/Stores/Bases/AbstractStore.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntity.php b/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntity.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Bases/DoctrineDbal/AbstractStore.php b/src/Data/Stores/Bases/DoctrineDbal/AbstractStore.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Builders/Bases/AbstractStoreBuilder.php b/src/Data/Stores/Builders/Bases/AbstractStoreBuilder.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Builders/DataStoreBuilder.php b/src/Data/Stores/Builders/DataStoreBuilder.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Builders/JobsStoreBuilder.php b/src/Data/Stores/Builders/JobsStoreBuilder.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Connections/Bases/AbstractMigrator.php b/src/Data/Stores/Connections/Bases/AbstractMigrator.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php b/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php old mode 100644 new mode 100755 index f4e37ec68eab9a391121a862bb03ae8ade8faca2..73863b6ae2677e367233f95f986baf03cc8dcc76 --- a/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php +++ b/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php @@ -48,11 +48,14 @@ abstract class AbstractMigration implements MigrationInterface * Prepare prefixed table name which will include table prefix from connection, local table prefix, and table name. * * @param string $tableName + * @param string|null $tablePrefixOverride * @return string */ - protected function preparePrefixedTableName(string $tableName): string + protected function preparePrefixedTableName(string $tableName, string $tablePrefixOverride = null): string { - return $this->connection->preparePrefixedTableName($this->getLocalTablePrefix() . $tableName); + $tablePrefix = $tablePrefixOverride ?? $this->getLocalTablePrefix(); + + return $this->connection->preparePrefixedTableName($tablePrefix . $tableName); } /** diff --git a/src/Data/Stores/Connections/DoctrineDbal/Connection.php b/src/Data/Stores/Connections/DoctrineDbal/Connection.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Connections/DoctrineDbal/Factory.php b/src/Data/Stores/Connections/DoctrineDbal/Factory.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Connections/DoctrineDbal/Migrator.php b/src/Data/Stores/Connections/DoctrineDbal/Migrator.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/ActivityInterface.php b/src/Data/Stores/Interfaces/ActivityInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/ConnectedServicesInterface.php b/src/Data/Stores/Interfaces/ConnectedServicesInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/ConnectionInterface.php b/src/Data/Stores/Interfaces/ConnectionInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/DataStoreInterface.php b/src/Data/Stores/Interfaces/DataStoreInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/JobsStoreInterface.php b/src/Data/Stores/Interfaces/JobsStoreInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/MigrationInterface.php b/src/Data/Stores/Interfaces/MigrationInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Interfaces/StoreInterface.php b/src/Data/Stores/Interfaces/StoreInterface.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store.php b/src/Data/Stores/Jobs/DoctrineDbal/Store.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJob.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJob.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/Repository.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Repository.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/TableConstants.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/TableConstants.php old mode 100644 new mode 100755 diff --git a/src/Data/Stores/Jobs/PhpRedis/RedisStore.php b/src/Data/Stores/Jobs/PhpRedis/RedisStore.php old mode 100644 new mode 100755 diff --git a/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTracker.php b/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTracker.php new file mode 100644 index 0000000000000000000000000000000000000000..9013533708e11fa4e1c8e7cb199876d4c70e58a9 --- /dev/null +++ b/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTracker.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal; + +use DateInterval; +use DateTimeImmutable; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\CurrentDataProvider; +use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\ModuleConfiguration; + +class CurrentDataTracker extends CurrentDataProvider implements DataTrackerInterface +{ + /** + * @throws StoreException + */ + public static function build( + ModuleConfiguration $moduleConfiguration, + LoggerInterface $logger, + string $connectionType = ModuleConfiguration\ConnectionType::MASTER + ): self { + return new self($moduleConfiguration, $logger, $connectionType); + } + + /** + * @throws StoreException + */ + public function process(Event $authenticationEvent): void + { + $this->store->persist($authenticationEvent); + } + + /** + * @throws StoreException + */ + public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void + { + $dateTime = (new DateTimeImmutable())->sub($retentionPolicy); + + $this->store->deleteDataOlderThan($dateTime); + } +} diff --git a/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php b/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php old mode 100644 new mode 100755 index 8f30682233ad5f30284d94da2d16c25a15ef8f40..8759f0841af4b91d2f697ec4fbcf3701c68839e5 --- a/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php +++ b/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php @@ -26,11 +26,17 @@ class VersionedDataTracker extends VersionedDataProvider implements DataTrackerI return new self($moduleConfiguration, $logger, $connectionType); } + /** + * @throws StoreException + */ public function process(Event $authenticationEvent): void { $this->store->persist($authenticationEvent); } + /** + * @throws StoreException + */ public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void { $dateTime = (new DateTimeImmutable())->sub($retentionPolicy); diff --git a/src/Data/Trackers/Builders/DataTrackerBuilder.php b/src/Data/Trackers/Builders/DataTrackerBuilder.php old mode 100644 new mode 100755 diff --git a/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php b/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php old mode 100644 new mode 100755 index 0ff530fcada5f5aa1916c2efc5fad8e3eb95d89b..5ab79192259141d4e7ba9c0a265f6a31736f7440 --- a/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php +++ b/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php @@ -6,6 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineD use DateInterval; use DateTimeImmutable; +use Doctrine\DBAL\Exception; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider; use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; @@ -26,6 +27,10 @@ class CurrentDataTracker extends CurrentDataProvider implements DataTrackerInter return new self($moduleConfiguration, $logger, $connectionType); } + /** + * @throws StoreException + * @throws Exception + */ public function process(Event $authenticationEvent): void { $this->store->persist($authenticationEvent); diff --git a/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php b/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php old mode 100644 new mode 100755 index 96013b0974ac4eed693db57b36d8a4a7634c432b..d324ab83b4b83416919be9e6e05ddebbdbbcf827 --- a/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php +++ b/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php @@ -6,6 +6,7 @@ namespace SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineD use DateInterval; use DateTimeImmutable; +use Doctrine\DBAL\Exception; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider; use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; @@ -26,6 +27,10 @@ class VersionedDataTracker extends VersionedDataProvider implements DataTrackerI return new self($moduleConfiguration, $logger, $connectionType); } + /** + * @throws StoreException + * @throws Exception + */ public function process(Event $authenticationEvent): void { $this->store->persist($authenticationEvent); diff --git a/src/Data/Trackers/Interfaces/DataTrackerInterface.php b/src/Data/Trackers/Interfaces/DataTrackerInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Activity.php b/src/Entities/Activity.php old mode 100644 new mode 100755 diff --git a/src/Entities/Activity/Bag.php b/src/Entities/Activity/Bag.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Event.php b/src/Entities/Authentication/Event.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Event/Job.php b/src/Entities/Authentication/Event/Job.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Event/State/Oidc.php b/src/Entities/Authentication/Event/State/Oidc.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Event/State/Saml2.php b/src/Entities/Authentication/Event/State/Saml2.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Protocol/Bag.php b/src/Entities/Authentication/Protocol/Bag.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Protocol/Oidc.php b/src/Entities/Authentication/Protocol/Oidc.php old mode 100644 new mode 100755 diff --git a/src/Entities/Authentication/Protocol/Saml2.php b/src/Entities/Authentication/Protocol/Saml2.php old mode 100644 new mode 100755 diff --git a/src/Entities/Bases/AbstractJob.php b/src/Entities/Bases/AbstractJob.php old mode 100644 new mode 100755 diff --git a/src/Entities/Bases/AbstractPayload.php b/src/Entities/Bases/AbstractPayload.php old mode 100644 new mode 100755 diff --git a/src/Entities/Bases/AbstractProvider.php b/src/Entities/Bases/AbstractProvider.php old mode 100644 new mode 100755 diff --git a/src/Entities/Bases/AbstractState.php b/src/Entities/Bases/AbstractState.php old mode 100644 new mode 100755 diff --git a/src/Entities/ConnectedService.php b/src/Entities/ConnectedService.php old mode 100644 new mode 100755 diff --git a/src/Entities/ConnectedService/Bag.php b/src/Entities/ConnectedService/Bag.php old mode 100644 new mode 100755 diff --git a/src/Entities/GenericJob.php b/src/Entities/GenericJob.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/AuthenticationProtocolInterface.php b/src/Entities/Interfaces/AuthenticationProtocolInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/IdentityProviderInterface.php b/src/Entities/Interfaces/IdentityProviderInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/JobInterface.php b/src/Entities/Interfaces/JobInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/ProviderInterface.php b/src/Entities/Interfaces/ProviderInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/ServiceProviderInterface.php b/src/Entities/Interfaces/ServiceProviderInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Interfaces/StateInterface.php b/src/Entities/Interfaces/StateInterface.php old mode 100644 new mode 100755 diff --git a/src/Entities/Providers/Identity/Oidc.php b/src/Entities/Providers/Identity/Oidc.php old mode 100644 new mode 100755 diff --git a/src/Entities/Providers/Identity/Saml2.php b/src/Entities/Providers/Identity/Saml2.php old mode 100644 new mode 100755 diff --git a/src/Entities/Providers/Service/Oidc.php b/src/Entities/Providers/Service/Oidc.php old mode 100644 new mode 100755 diff --git a/src/Entities/Providers/Service/Saml2.php b/src/Entities/Providers/Service/Saml2.php old mode 100644 new mode 100755 diff --git a/src/Entities/User.php b/src/Entities/User.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/Exception.php b/src/Exceptions/Exception.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/InvalidConfigurationException.php b/src/Exceptions/InvalidConfigurationException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/InvalidValueException.php b/src/Exceptions/InvalidValueException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/MetadataException.php b/src/Exceptions/MetadataException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/StateException.php b/src/Exceptions/StateException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/StoreException.php b/src/Exceptions/StoreException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/StoreException/MigrationException.php b/src/Exceptions/StoreException/MigrationException.php old mode 100644 new mode 100755 diff --git a/src/Exceptions/UnexpectedValueException.php b/src/Exceptions/UnexpectedValueException.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Attributes.php b/src/Helpers/Attributes.php old mode 100644 new mode 100755 diff --git a/src/Helpers/AuthenticationEventStateResolver.php b/src/Helpers/AuthenticationEventStateResolver.php old mode 100644 new mode 100755 diff --git a/src/Helpers/DateTime.php b/src/Helpers/DateTime.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Environment.php b/src/Helpers/Environment.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Filesystem.php b/src/Helpers/Filesystem.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Hash.php b/src/Helpers/Hash.php old mode 100644 new mode 100755 diff --git a/src/Helpers/InstanceBuilderUsingModuleConfiguration.php b/src/Helpers/InstanceBuilderUsingModuleConfiguration.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Network.php b/src/Helpers/Network.php old mode 100644 new mode 100755 diff --git a/src/Helpers/ProviderResolver.php b/src/Helpers/ProviderResolver.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Random.php b/src/Helpers/Random.php old mode 100644 new mode 100755 diff --git a/src/Helpers/Routes.php b/src/Helpers/Routes.php old mode 100644 new mode 100755 diff --git a/src/Helpers/SspModule.php b/src/Helpers/SspModule.php old mode 100644 new mode 100755 index df5798a0f7c18fc57b3c7e2c0b5409da8615c06b..33819ab70dfe8de20d13f3d3df4a21ecd1f6b4a7 --- a/src/Helpers/SspModule.php +++ b/src/Helpers/SspModule.php @@ -15,6 +15,7 @@ class SspModule { try { return Module::isModuleEnabled($moduleName); + // @codeCoverageIgnoreStart } catch (\Throwable $exception) { $message = sprintf('Could not check if module %s is enabled', $moduleName); throw new Module\accounting\Exceptions\InvalidConfigurationException( @@ -23,5 +24,6 @@ class SspModule $exception ); } + // @codeCoverageIgnoreEnd } } diff --git a/src/Http/Controllers/Admin/Configuration.php b/src/Http/Controllers/Admin/Configuration.php old mode 100644 new mode 100755 diff --git a/src/Http/Controllers/User/Profile.php b/src/Http/Controllers/User/Profile.php old mode 100644 new mode 100755 diff --git a/src/Interfaces/BuildableUsingModuleConfigurationInterface.php b/src/Interfaces/BuildableUsingModuleConfigurationInterface.php old mode 100644 new mode 100755 diff --git a/src/Interfaces/SetupableInterface.php b/src/Interfaces/SetupableInterface.php old mode 100644 new mode 100755 diff --git a/src/ModuleConfiguration.php b/src/ModuleConfiguration.php old mode 100644 new mode 100755 diff --git a/src/ModuleConfiguration/AccountingProcessingType.php b/src/ModuleConfiguration/AccountingProcessingType.php old mode 100644 new mode 100755 diff --git a/src/ModuleConfiguration/ConnectionType.php b/src/ModuleConfiguration/ConnectionType.php old mode 100644 new mode 100755 diff --git a/src/Services/AlertsBag.php b/src/Services/AlertsBag.php old mode 100644 new mode 100755 diff --git a/src/Services/AlertsBag/Alert.php b/src/Services/AlertsBag/Alert.php old mode 100644 new mode 100755 diff --git a/src/Services/CsrfToken.php b/src/Services/CsrfToken.php old mode 100644 new mode 100755 diff --git a/src/Services/HelpersManager.php b/src/Services/HelpersManager.php old mode 100644 new mode 100755 diff --git a/src/Services/JobRunner.php b/src/Services/JobRunner.php old mode 100644 new mode 100755 diff --git a/src/Services/JobRunner/RateLimiter.php b/src/Services/JobRunner/RateLimiter.php old mode 100644 new mode 100755 diff --git a/src/Services/JobRunner/State.php b/src/Services/JobRunner/State.php old mode 100644 new mode 100755 diff --git a/src/Services/Logger.php b/src/Services/Logger.php old mode 100644 new mode 100755 diff --git a/src/Services/MenuManager.php b/src/Services/MenuManager.php old mode 100644 new mode 100755 diff --git a/src/Services/MenuManager/MenuItem.php b/src/Services/MenuManager/MenuItem.php old mode 100644 new mode 100755 diff --git a/src/Services/SspModuleManager.php b/src/Services/SspModuleManager.php old mode 100644 new mode 100755 diff --git a/src/Services/TrackerResolver.php b/src/Services/TrackerResolver.php old mode 100644 new mode 100755 diff --git a/src/SspModule/Oidc.php b/src/SspModule/Oidc.php old mode 100644 new mode 100755 diff --git a/src/Traits/HasUserAttributesTrait.php b/src/Traits/HasUserAttributesTrait.php old mode 100644 new mode 100755 diff --git a/templates/admin/configuration/status.twig b/templates/admin/configuration/status.twig old mode 100644 new mode 100755 index d8b00d9e62461235ce75cded91061ce922783201..1dac6f8635e443a5125a732fab48cb0c9facb70e --- a/templates/admin/configuration/status.twig +++ b/templates/admin/configuration/status.twig @@ -33,7 +33,7 @@ {% if providers is not empty %} <li> - <strong>{{ 'Providers setup'|trans }}</strong>: + <strong>{{ 'Providers setup needed'|trans }}</strong>: <ul> {% for providerClass, providerInstance in providers %} <li> diff --git a/templates/base.twig b/templates/base.twig old mode 100644 new mode 100755 diff --git a/templates/includes/_alerts.twig b/templates/includes/_alerts.twig old mode 100644 new mode 100755 diff --git a/templates/includes/_header.twig b/templates/includes/_header.twig old mode 100644 new mode 100755 diff --git a/templates/includes/_navigation.twig b/templates/includes/_navigation.twig old mode 100644 new mode 100755 diff --git a/templates/user/activity.twig b/templates/user/activity.twig old mode 100644 new mode 100755 diff --git a/templates/user/activity.twig.bak b/templates/user/activity.twig.bak old mode 100644 new mode 100755 diff --git a/templates/user/connected-organizations.twig b/templates/user/connected-organizations.twig old mode 100644 new mode 100755 diff --git a/templates/user/connected-organizations.twig.bak b/templates/user/connected-organizations.twig.bak old mode 100644 new mode 100755 diff --git a/templates/user/personal-data.twig b/templates/user/personal-data.twig old mode 100644 new mode 100755 diff --git a/templates/user/personal-data.twig.bak b/templates/user/personal-data.twig.bak old mode 100644 new mode 100755 diff --git a/tests/attributemap/test.php b/tests/attributemap/test.php old mode 100644 new mode 100755 diff --git a/tests/attributemap/test2.php b/tests/attributemap/test2.php old mode 100644 new mode 100755 diff --git a/tests/config-templates/config.php b/tests/config-templates/config.php old mode 100644 new mode 100755 diff --git a/tests/config-templates/module_accounting.php b/tests/config-templates/module_accounting.php old mode 100644 new mode 100755 diff --git a/tests/config-templates/module_oidc.php b/tests/config-templates/module_oidc.php new file mode 100644 index 0000000000000000000000000000000000000000..2452edbecbb1f3b0bc3952a9c95ada21bcc7bd49 --- /dev/null +++ b/tests/config-templates/module_oidc.php @@ -0,0 +1,253 @@ +<?php + +/* + * This file is part of the simplesamlphp-module-oidc. + * + * Copyright (C) 2018 by the Spanish Research and Academic Network. + * + * This code was developed by Universidad de Córdoba (UCO https://www.uco.es) + * for the RedIRIS SIR service (SIR: http://www.rediris.es/sir) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$config = [ + // pagination + 'items_per_page' => 20, + + // The private key passphrase (optional) + // 'pass_phrase' => 'secret', + // The cert and key for signing the ID token. Default names are oidc_module.key and oidc_module.crt + // 'privatekey' => 'oidc_module.key', + // 'certificate' => 'oidc_module.crt', + + // Tokens TTL + 'authCodeDuration' => 'PT10M', // 10 minutes + 'refreshTokenDuration' => 'P1M', // 1 month + 'accessTokenDuration' => 'PT1H', // 1 hour, + + // Tag to run storage cleanup script using the cron module... + 'cron_tag' => 'hourly', + + // Set token signer + // See Lcobucci\JWT\Signer algorithms in https://github.com/lcobucci/jwt/tree/master/src/Signer + 'signer' => \Lcobucci\JWT\Signer\Rsa\Sha256::class, + // 'signer' => \Lcobucci\JWT\Signer\Hmac\Sha256::class, + // 'signer' => \Lcobucci\JWT\Signer\Ecdsa\Sha256::class, + + // The key id to use in the header. Default is a finger print of the public key + // 'kid' => 'abcd', + + + // this is the default auth source used for authentication if the auth source + // is not specified on particular client + 'auth' => 'default-sp', + + // useridattr is the attribute-name that contains the userid as returned from idp. By default, this attribute + // will be dynamically added to the 'sub' claim in the attribute-to-claim translation table (you will probably + // want to use this attribute as the 'sub' claim since it designates unique identifier for the user). + 'useridattr' => 'uid', + + /** + * Permissions let the module expose functionality to specific users. + * In the below configuration, a user's eduPersonEntitlement attribute is examined. If the user + * tries to do something that requires the 'client' permission (such as registering their own client) + * then they will need one of the eduPersonEntitlements from the `client` permission array. + * + * A permission can be disable by commenting it out. + */ + 'permissions' => [ + // Attribute to inspect to determine user's permissions + 'attribute' => 'eduPersonEntitlement', + // Which entitlements allow for registering, editing, delete a client. OIDC clients are owned by the creator + 'client' => ['urn:example:oidc:manage:client'], + ], + + // Settings regarding Authentication Processing Filters. + // Note: OIDC authN state array will not contain all of the keys which are available during SAML authN, + // like Service Provider metadata, etc. + // + // At the moment, the following SAML authN data will be available during OIDC authN in the sate array: + // - ['Attributes'], ['Authority'], ['AuthnInstant'], ['Expire'] + // Source and destination will have entity IDs corresponding to the OP issuer ID and Client ID respectively. + // - ['Source']['entityid'] - contains OpenId Provider issuer ID + // - ['Destination']['entityid'] - contains Relying Party (OIDC Client) ID + // In addition to that, the following OIDC related data will be available in the state array: + // - ['Oidc']['OpenIdProviderMetadata'] - contains information otherwise available from the OIDC configuration URL. + // - ['Oidc']['RelyingPartyMetadata'] - contains information about the OIDC client making the authN request. + // - ['Oidc']['AuthorizationRequestParameters'] - contains relevant authorization request query parameters. + // + // List of authproc filters which will run for every OIDC authN. Add filters as described in docs for SAML authproc + // @see https://simplesamlphp.org/docs/stable/simplesamlphp-authproc + 'authproc.oidc' => [ + // Add authproc filters here + ], + + // Optional custom scopes. You can create as many scopes as you want and assign claims to them. + 'scopes' => [ +// 'private' => [ // The key represents the scope name. +// 'description' => 'private scope', +// 'claim_name_prefix' => '', // Prefix to apply for all claim names from this scope +// 'are_multiple_claim_values_allowed' => false, // Are claims for this scope allowed to have multiple values +// 'claims' => ['national_document_id'] // Claims from the translation table which this scope will contain +// ], + ], + 'translate' => [ + /* + * This is the default translate table from SAML to OIDC. + * You can change here the behaviour or add more translation to your + * private attributes scopes + * + * The basic format is + * + * 'claimName' => [ + * 'type' => 'string|int|bool|json', + * // For non JSON types + * 'attributes' => ['samlAttribute1', 'samlAttribute2'] + * // For JSON types + * 'claims => [ + * 'subclaim' => [ 'type' => 'string', 'attributes' => ['saml1']] + * ] + * ] + * + * For convenience the default type is "string" so type does not need to be defined. + * If "attributes" is not set, then it is assumed that the rest of the values are saml + * attribute names. + * + * Note on 'sub' claim: by default, the list of attributes for 'sub' claim will also contain attribute defined + * in 'useridattr' setting. You will probably want to use this attribute as the 'sub' claim since it + * designates unique identifier for the user, However, override as necessary. + */ +// 'sub' => [ +// 'attribute-defined-in-useridattr', // will be dynamically added if the list for 'sub' claim is not set. +// 'eduPersonPrincipalName', +// 'eduPersonTargetedID', +// 'eduPersonUniqueId', +// ], +// 'name' => [ +// 'cn', +// 'displayName', +// ], +// 'family_name' => [ +// 'sn', +// ], +// 'given_name' => [ +// 'givenName', +// ], +// 'middle_name' => [ +// // Empty +// ], +// 'nickname' => [ +// 'eduPersonNickname', +// ], +// 'preferred_username' => [ +// 'uid', +// ], +// 'profile' => [ +// 'labeledURI', +// 'description', +// ], +// 'picture' => [ +// // Empty. Previously 'jpegPhoto' however spec calls for a url to photo, not an actual photo. +// ], +// 'website' => [ +// // Empty +// ], +// 'gender' => [ +// // Empty +// ], +// 'birthdate' => [ +// // Empty +// ], +// 'zoneinfo' => [ +// // Empty +// ], +// 'locale' => [ +// 'preferredLanguage', +// ], +// 'updated_at' => [ +// 'type' => 'int', +// 'attributes' => [], +// ], +// 'email' => [ +// 'mail', +// ], +// 'email_verified' => [ +// 'type' => 'bool', +// 'attributes' => [], +// ], +// // address is a json object. Set the 'formatted' sub claim to postalAddress +// 'address' => [ +// 'type' => 'json', +// 'claims' => [ +// 'formatted' => ['postalAddress'], +// ] +// ], +// 'phone_number' => [ +// 'mobile', +// 'telephoneNumber', +// 'homePhone', +// ], +// 'phone_number_verified' => [ +// 'type' => 'bool', +// 'attributes' => [], +// ], + /* + * Optional scopes attributes + */ +// 'national_document_id' => [ +// 'schacPersonalUniqueId', +// ], + ], + + // Optional list of the Authentication Context Class References that this OP supports. + // If populated, this list will be available in OP discovery document (OP Metadata) as 'acr_values_supported'. + // @see https://datatracker.ietf.org/doc/html/rfc6711 + // @see https://www.iana.org/assignments/loa-profiles/loa-profiles.xhtml + // @see https://openid.net/specs/openid-connect-core-1_0.html#IDToken (acr claim) + // @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest (acr_values parameter) + // Syntax: string[] (array of strings) + 'acrValuesSupported' => [ +// 'https://refeds.org/assurance/profile/espresso', +// 'https://refeds.org/assurance/profile/cappuccino', +// 'https://refeds.org/profile/mfa', +// 'https://refeds.org/profile/sfa', +// 'urn:mace:incommon:iap:silver', +// 'urn:mace:incommon:iap:bronze', +// '4', +// '3', +// '2', +// '1', +// '0', +// '...', + ], + + // If this OP supports ACRs, indicate which usable auth source supports which ACRs. + // Order of ACRs is important, more important ones being first. + // Syntax: array<string,string[]> (array with auth source as key and value being array of ACR values as strings) + 'authSourcesToAcrValuesMap' => [ +// 'example-userpass' => ['1', '0'], +// 'default-sp' => ['http://id.incommon.org/assurance/bronze', '2', '1', '0'], +// 'strongly-assured-authsource' => [ +// 'https://refeds.org/assurance/profile/espresso', +// 'https://refeds.org/profile/mfa', +// 'https://refeds.org/assurance/profile/cappuccino', +// 'https://refeds.org/profile/sfa', +// '3', +// '2', +// '1', +// '0', +// ], + ], + + // If this OP supports ACRs, indicate if authentication using cookie should be forced to specific ACR value. + // If this option is set to null, no specific ACR will be forced for cookie authentication and the resulting ACR + // will be one of the ACRs supported on used auth source during authentication, that is, session creation. + // If this option is set to specific ACR, with ACR value being one of the ACR value this OP supports, it will be + // set to that ACR for cookie authentication. + // For example, OIDC Core Spec notes that authentication using a long-lived browser cookie is one example where + // the use of "level 0" is appropriate: +// 'forcedAcrValueForCookieAuthentication' => '0', + 'forcedAcrValueForCookieAuthentication' => null, +]; diff --git a/tests/src/Auth/Process/AccountingTest.php b/tests/src/Auth/Process/AccountingTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Constants/ConnectionParameters.php b/tests/src/Constants/ConnectionParameters.php old mode 100644 new mode 100755 diff --git a/tests/src/Constants/DateTime.php b/tests/src/Constants/DateTime.php old mode 100644 new mode 100755 diff --git a/tests/src/Constants/RawRowResult.php b/tests/src/Constants/RawRowResult.php old mode 100644 new mode 100755 index faa0e7ee6d652fd07c8fe8d7038f65a0c7f5b93b..7f05120eed13d774dc8d64e5f380c14056816079 --- a/tests/src/Constants/RawRowResult.php +++ b/tests/src/Constants/RawRowResult.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace SimpleSAML\Test\Module\accounting\Constants; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants as ActivityTableConstants; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants as ConnectedServicesTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants as ActivityTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants as ConnectedServicesTableConstants; class RawRowResult { diff --git a/tests/src/Constants/SerializedJob.php b/tests/src/Constants/SerializedJob.php old mode 100644 new mode 100755 diff --git a/tests/src/Constants/StateArrays.php b/tests/src/Constants/StateArrays.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProviderTest.php b/tests/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProviderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..96d2eeab9f8fc08f733b0a41bcaef99974458238 --- /dev/null +++ b/tests/src/Data/Providers/Activity/DoctrineDbal/CurrentDataProviderTest.php @@ -0,0 +1,155 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Providers\Activity\DoctrineDbal; + +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\CurrentDataProvider; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\CurrentDataTracker; +use SimpleSAML\Module\accounting\Entities\Activity\Bag; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\CurrentDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + */ +class CurrentDataProviderTest extends TestCase +{ + /** + * @var MockObject + */ + protected $moduleConfigurationMock; + /** + * @var MockObject + */ + protected $loggerMock; + /** + * @var MockObject + */ + protected $storeMock; + /** + * @var MockObject + */ + protected $activityBagMock; + + protected function setUp(): void + { + $this->moduleConfigurationMock = $this->createMock(ModuleConfiguration::class); + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationMock->method('getConnectionParameters') + ->willReturn($connectionParams); + + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->storeMock = $this->createMock(Store::class); + + $this->activityBagMock = $this->createMock(Bag::class); + } + + protected function prepareMockedInstance(): CurrentDataProvider + { + return new CurrentDataProvider( + $this->moduleConfigurationMock, + $this->loggerMock, + ModuleConfiguration\ConnectionType::SLAVE, + $this->storeMock + ); + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf(CurrentDataProvider::class, $this->prepareMockedInstance()); + } + + /** + * @throws StoreException + */ + public function testCanSelfBuild(): void + { + $this->assertInstanceOf( + CurrentDataProvider::class, + CurrentDataProvider::build($this->moduleConfigurationMock, $this->loggerMock) + ); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupReturnsTrue(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + + $this->assertTrue($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupReturnsFalse(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + + $this->assertFalse($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupLogsWarningIfNotNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + $this->loggerMock->expects($this->once())->method('warning'); + + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupIfNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + $this->storeMock->expects($this->once())->method('runSetup'); + + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + */ + public function testGetActivitiy(): void + { + $this->storeMock->expects($this->once()) + ->method('getActivity') + ->with('userId', 10, 0) + ->willReturn($this->activityBagMock); + + $this->prepareMockedInstance()->getActivity('userId', 10, 0); + } + + /** + * @throws StoreException + */ + public function testGetTracker(): void + { + $this->assertInstanceOf( + CurrentDataTracker::class, + $this->prepareMockedInstance()->getTracker() + ); + } +} diff --git a/tests/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProviderTest.php b/tests/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProviderTest.php old mode 100644 new mode 100755 index d505423f99cb424025b1638e4e3d1a4b87d46f69..befe42f59d0d87bbc25ec4a89af60b21ca10152a --- a/tests/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProviderTest.php +++ b/tests/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProviderTest.php @@ -9,22 +9,25 @@ use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider; use PHPUnit\Framework\TestCase; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store; -use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; +use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker; +use SimpleSAML\Module\accounting\Entities\Activity\Bag; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; /** * @covers \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider - * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator - * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory - * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection - * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator - * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore - * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore - * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store - * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator */ class VersionedDataProviderTest extends TestCase { @@ -40,12 +43,22 @@ class VersionedDataProviderTest extends TestCase * @var MockObject */ protected $storeMock; + /** + * @var MockObject + */ + protected $activityBagMock; protected function setUp(): void { $this->moduleConfigurationMock = $this->createMock(ModuleConfiguration::class); + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationMock->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->loggerMock = $this->createMock(LoggerInterface::class); $this->storeMock = $this->createMock(Store::class); + + $this->activityBagMock = $this->createMock(Bag::class); } protected function prepareMockedInstance(): VersionedDataProvider @@ -61,64 +74,83 @@ class VersionedDataProviderTest extends TestCase public function testCanCreateInstance(): void { $this->assertInstanceOf(VersionedDataProvider::class, $this->prepareMockedInstance()); + } - $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; - $this->moduleConfigurationMock->method('getConnectionParameters') - ->willReturn($connectionParams); - - $this->assertInstanceOf(VersionedDataProvider::class, VersionedDataProvider::build( - $this->moduleConfigurationMock, - $this->loggerMock, - )); + /** + * @throws StoreException + */ + public function testCanSelfBuild(): void + { + $this->assertInstanceOf( + VersionedDataProvider::class, + VersionedDataProvider::build($this->moduleConfigurationMock, $this->loggerMock) + ); } - public function testNeedsSetup(): void + /** + * @throws StoreException + */ + public function testNeedsSetupReturnsTrue(): void { $this->storeMock->method('needsSetup')->willReturn(true); + $this->assertTrue($this->prepareMockedInstance()->needsSetup()); } - public function testDoesNotNeedsSetup(): void + /** + * @throws StoreException + */ + public function testNeedsSetupReturnsFalse(): void { $this->storeMock->method('needsSetup')->willReturn(false); + $this->assertFalse($this->prepareMockedInstance()->needsSetup()); } - public function testSetupDoesNotRunWhenNotNeeded(): void + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupLogsWarningIfNotNeeded(): void { $this->storeMock->method('needsSetup')->willReturn(false); - $this->loggerMock->expects($this->once()) - ->method('warning'); - $this->storeMock->expects($this->never()) - ->method('runSetup'); + $this->loggerMock->expects($this->once())->method('warning'); $this->prepareMockedInstance()->runSetup(); } - public function testSetupRunsWhenNeeded(): void + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupIfNeeded(): void { $this->storeMock->method('needsSetup')->willReturn(true); $this->storeMock->expects($this->once())->method('runSetup'); + $this->prepareMockedInstance()->runSetup(); } - public function testGetsActivityFromStore(): void + /** + * @throws StoreException + */ + public function testGetActivitiy(): void { $this->storeMock->expects($this->once()) ->method('getActivity') - ->with('1', 2, 3); + ->with('userId', 10, 0) + ->willReturn($this->activityBagMock); - $this->prepareMockedInstance()->getActivity('1', 2, 3); + $this->prepareMockedInstance()->getActivity('userId', 10, 0); } - public function testCanGetTracker(): void + /** + * @throws StoreException + */ + public function testGetTracker(): void { - $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; - $this->moduleConfigurationMock->method('getConnectionParameters') - ->willReturn($connectionParams); - $this->assertInstanceOf( - DataTrackerInterface::class, + VersionedDataTracker::class, $this->prepareMockedInstance()->getTracker() ); } diff --git a/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php b/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php old mode 100644 new mode 100755 index 7b2b5ac7a031a3b6cad82420c15a3b94a3d607d7..64e3c4a9570d8e8ad4d6493f16545f5a50a3e40a --- a/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php +++ b/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php @@ -7,9 +7,13 @@ namespace SimpleSAML\Test\Module\accounting\Data\Providers\Builders; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider as + ActivityVersionedDataProviderAlias; use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder; -use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker; +use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider as + ConnectedServicesVersionedDataProviderAlias; use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; @@ -25,13 +29,15 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store - * @uses \SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator * @uses \SimpleSAML\Module\accounting\Services\HelpersManager * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore * @uses \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository */ class DataProviderBuilderTest extends TestCase { @@ -51,15 +57,20 @@ class DataProviderBuilderTest extends TestCase $this->helpersManager = new HelpersManager(); } + protected function prepareMockedInstance(): DataProviderBuilder + { + return new DataProviderBuilder( + $this->moduleConfigurationStub, + $this->loggerStub, + $this->helpersManager + ); + } + public function testCanCreateInstance(): void { $this->assertInstanceOf( DataProviderBuilder::class, - new DataProviderBuilder( - $this->moduleConfigurationStub, - $this->loggerStub, - $this->helpersManager - ) + $this->prepareMockedInstance() ); } @@ -68,23 +79,53 @@ class DataProviderBuilderTest extends TestCase */ public function testCanBuildDataProvider(): void { - $builder = new DataProviderBuilder( - $this->moduleConfigurationStub, - $this->loggerStub, - $this->helpersManager - ); + $builder = $this->prepareMockedInstance(); - $this->assertInstanceOf(VersionedDataTracker::class, $builder->build(VersionedDataTracker::class)); + $this->assertInstanceOf( + ActivityVersionedDataProviderAlias::class, + $builder->build(ActivityVersionedDataProviderAlias::class) + ); } public function testThrowsForInvalidClass(): void { $this->expectException(Exception::class); - (new DataProviderBuilder( - $this->moduleConfigurationStub, - $this->loggerStub, - $this->helpersManager - ))->build('invalid'); + $this->prepareMockedInstance()->build('invalid'); + } + + /** + * @throws Exception + */ + public function testCanBuildActivityProvider(): void + { + $this->assertInstanceOf( + ActivityVersionedDataProviderAlias::class, + $this->prepareMockedInstance()->buildActivityProvider(ActivityVersionedDataProviderAlias::class) + ); + } + + public function testBuildActivityProviderThrowsForInvalidClass(): void + { + $this->expectException(UnexpectedValueException::class); + + $this->prepareMockedInstance()->buildActivityProvider(ConnectedServicesVersionedDataProviderAlias::class); + } + + public function testCanBuildConnectedServicesProvider(): void + { + $this->assertInstanceOf( + ConnectedServicesVersionedDataProviderAlias::class, + $this->prepareMockedInstance()->buildConnectedServicesProvider( + ConnectedServicesVersionedDataProviderAlias::class + ) + ); + } + + public function testBuildConnectedServicesProviderThrowsForInvalidClass(): void + { + $this->expectException(UnexpectedValueException::class); + + $this->prepareMockedInstance()->buildConnectedServicesProvider(ActivityVersionedDataProviderAlias::class); } } diff --git a/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProviderTest.php b/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProviderTest.php new file mode 100755 index 0000000000000000000000000000000000000000..38a4246e22e5039c7432345cb91ce52148be6492 --- /dev/null +++ b/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProviderTest.php @@ -0,0 +1,151 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal; + +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider; +use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Entities\ConnectedService\Bag; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + */ +class CurrentDataProviderTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $moduleConfigurationMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $loggerMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $storeMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $connectedServicesBagMock; + + protected function setUp(): void + { + $this->moduleConfigurationMock = $this->createMock(ModuleConfiguration::class); + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationMock->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->storeMock = $this->createMock(Store::class); + + $this->connectedServicesBagMock = $this->createMock(Bag::class); + } + + /** + * @throws StoreException + */ + protected function prepareMockedInstance(): CurrentDataProvider + { + return new CurrentDataProvider( + $this->moduleConfigurationMock, + $this->loggerMock, + ModuleConfiguration\ConnectionType::SLAVE, + $this->storeMock + ); + } + + /** + * @throws StoreException + */ + public function testCanCreateInstance(): void + { + $this->assertInstanceOf(CurrentDataProvider::class, $this->prepareMockedInstance()); + } + + /** + * @throws StoreException + */ + public function testCanSelfBuild(): void + { + $this->assertInstanceOf( + CurrentDataProvider::class, + CurrentDataProvider::build($this->moduleConfigurationMock, $this->loggerMock) + ); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupWhenTrue(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + $this->assertTrue($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupWhenFalse(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + $this->assertFalse($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupLogsWarningWhenNotNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + $this->loggerMock->expects($this->once())->method('warning'); + + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testCanRunSetupWhenNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + $this->storeMock->expects($this->once())->method('runSetup'); + + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + */ + public function testCanGetConnectedServices(): void + { + $this->storeMock->expects($this->once())->method('getConnectedServices')->with('userId'); + + $this->prepareMockedInstance()->getConnectedServices('userId'); + } + + public function testCanGetTracker(): void + { + $this->assertInstanceOf( + CurrentDataProvider::class, + $this->prepareMockedInstance()->getTracker() + ); + } +} diff --git a/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProviderTest.php b/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProviderTest.php new file mode 100755 index 0000000000000000000000000000000000000000..0e10822a0dc7efe315e246116a2d405c5e626680 --- /dev/null +++ b/tests/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProviderTest.php @@ -0,0 +1,153 @@ +<?php + +declare(strict_types=1); + +namespace SimpleSAML\Test\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal; + +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store; +use SimpleSAML\Module\accounting\Entities\ConnectedService\Bag; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + */ +class VersionedDataProviderTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $moduleConfigurationMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $loggerMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $storeMock; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $connectedServicesBagMock; + protected function setUp(): void + { + $this->moduleConfigurationMock = $this->createMock(ModuleConfiguration::class); + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationMock->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->storeMock = $this->createMock(Store::class); + + $this->connectedServicesBagMock = $this->createMock(Bag::class); + } + + /** + * @throws StoreException + */ + public function prepareMockedInstance(): VersionedDataProvider + { + return new VersionedDataProvider( + $this->moduleConfigurationMock, + $this->loggerMock, + ModuleConfiguration\ConnectionType::SLAVE, + $this->storeMock + ); + } + + /** + * @throws StoreException + */ + public function testCanCreateInstance(): void + { + $this->assertInstanceOf(VersionedDataProvider::class, $this->prepareMockedInstance()); + } + + /** + * @throws StoreException + */ + public function testCanSelfBuild(): void + { + $this->assertInstanceOf( + VersionedDataProvider::class, + VersionedDataProvider::build($this->moduleConfigurationMock, $this->loggerMock) + ); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupWhenTrue(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + $this->assertTrue($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + */ + public function testNeedsSetupWhenFalse(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + $this->assertFalse($this->prepareMockedInstance()->needsSetup()); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testRunSetupLogsWarningWhenNotNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(false); + $this->loggerMock->expects($this->once())->method('warning'); + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + * @throws MigrationException + */ + public function testCanRunSetupWhenNeeded(): void + { + $this->storeMock->method('needsSetup')->willReturn(true); + $this->storeMock->expects($this->once())->method('runSetup'); + $this->prepareMockedInstance()->runSetup(); + } + + /** + * @throws StoreException + */ + public function testCanGetConnectedServices(): void + { + $this->storeMock->expects($this->once())->method('getConnectedServices')->with('userId'); + $this->storeMock->method('getConnectedServices')->willReturn($this->connectedServicesBagMock); + + $this->assertInstanceOf(Bag::class, $this->prepareMockedInstance()->getConnectedServices('userId')); + } + + /** + * @throws StoreException + */ + public function testCanGetTracker(): void + { + $this->assertInstanceOf( + VersionedDataProvider::class, + $this->prepareMockedInstance()->getTracker() + ); + } +} diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c8e44504077a184ba63f95cee44a5d926442d852 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php @@ -0,0 +1,103 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations; + +// phpcs:ignore +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000700CreateAuthenticationEventTable; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000700CreateAuthenticationEventTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + */ +class Version20220801000700CreateAuthenticationEventTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'cds_authentication_event'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws MigrationException + * @throws Exception + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new Version20220801000700CreateAuthenticationEventTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } + + /** + * @throws StoreException + */ + public function testRunThrowsMigrationException(): void + { + $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName); + $this->schemaManagerStub->method('createTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Version20220801000700CreateAuthenticationEventTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } + + /** + * @throws StoreException + */ + public function testRevertThrowsMigrationException(): void + { + $this->schemaManagerStub->method('dropTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Version20220801000700CreateAuthenticationEventTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->revert(); + } + + /** + * @throws StoreException + */ + public function testRunThrowsOnIvalidTableNameIdp(): void + { + $this->connectionStub->method('preparePrefixedTableName') + ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table + + $migration = new Version20220801000700CreateAuthenticationEventTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } +} diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/RepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..aa869d6d1ed95dda28a010c0290e635c05a79465 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/Store/RepositoryTest.php @@ -0,0 +1,289 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; + +use DateInterval; +use DateTimeImmutable; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\DateTime; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedBaseTableConstants; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000700CreateAuthenticationEventTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + * @uses \SimpleSAML\Module\accounting\ModuleConfiguration + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + */ +class RepositoryTest extends TestCase +{ + protected Connection $connection; + protected Stub $loggerStub; + protected Migrator $migrator; + protected string $dateTimeFormat; + protected string $idpEntityId; + protected string $idpEntityIdHash; + protected string $idpMetadata; + protected string $idpMetadataHash; + protected string $spEntityId; + protected string $spMetadataHash; + protected string $userIdentifier; + protected string $userIdentifierHash; + protected string $userAttributes; + protected string $userAttributesHash; + protected Repository $repository; + protected DateTimeImmutable $createdAt; + protected Stub $connectionStub; + protected string $spEntityIdHash; + protected string $spMetadata; + protected string $clientIpAddress; + protected string $authenticationProtocolDesignation; + + /** + * @throws StoreException + * @throws MigrationException + */ + protected function setUp(): void + { + // For stubbing. + $this->connectionStub = $this->createStub(Connection::class); + $this->loggerStub = $this->createStub(LoggerInterface::class); + + // For real DB testing. + $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->connection = new Connection($connectionParameters); + $this->migrator = new Migrator($this->connection, $this->loggerStub); + $moduleConfiguration = new ModuleConfiguration(); + $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR . + 'Data' . DIRECTORY_SEPARATOR . + 'Stores' . DIRECTORY_SEPARATOR . + 'Accounting' . DIRECTORY_SEPARATOR . + 'Activity' . DIRECTORY_SEPARATOR . + 'DoctrineDbal' . DIRECTORY_SEPARATOR . + 'Current' . DIRECTORY_SEPARATOR . + 'Store' . DIRECTORY_SEPARATOR . + AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + + $this->migrator->runSetup(); + $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace); + + $this->repository = new Repository($this->connection, $this->loggerStub); + + $this->dateTimeFormat = DateTime::DEFAULT_FORMAT; + + $this->idpEntityId = 'idp-entity-id'; + $this->idpEntityIdHash = 'idp-entity-id-hash'; + + $this->idpMetadata = 'idp-metadata'; + $this->idpMetadataHash = 'idp-metadata-hash'; + + $this->spEntityId = 'sp-entity-id'; + $this->spEntityIdHash = 'sp-entity-id-hash'; + + $this->spMetadata = 'sp-metadata'; + $this->spMetadataHash = 'sp-metadata-hash'; + + $this->userIdentifier = 'user-identifier'; + $this->userIdentifierHash = 'user-identifier-hash'; + + $this->userAttributes = 'user-attributes'; + $this->userAttributesHash = 'user-attributes-hash'; + + $this->createdAt = new DateTimeImmutable(); + $this->clientIpAddress = '123.123.123.123'; + $this->authenticationProtocolDesignation = Saml2::DESIGNATION; + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + Repository::class, + new Repository($this->connection, $this->loggerStub) + ); + } + + public function testInsertAuthenticationEventThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->insertAuthenticationEvent(1, 1, $this->createdAt); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanGetActivity(): void + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spEntityIdHash, + $this->createdAt + ); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + + $userVersionId = (int)$userVersionResult[VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $this->createdAt, + $this->clientIpAddress, + $this->authenticationProtocolDesignation, + $this->createdAt + ); + + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(1, $resultArray); + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $this->createdAt, + $this->clientIpAddress, + $this->authenticationProtocolDesignation, + $this->createdAt + ); + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(2, $resultArray); + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $this->createdAt, + $this->clientIpAddress, + $this->authenticationProtocolDesignation, + $this->createdAt + ); + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(3, $resultArray); + + // Simulate another SP + $spEntityIdNew = $this->spEntityId . '-new'; + $spEntityIdHashNew = $this->spEntityIdHash . '-new'; + $spMetadataNew = $this->spMetadata . '-new'; + $spMetadataHashNew = $this->spMetadataHash . '-new'; + $this->repository->insertSp( + $spEntityIdNew, + $spEntityIdHashNew, + $spMetadataNew, + $spMetadataHashNew, + $this->createdAt + ); + $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $this->createdAt, + $this->clientIpAddress, + $this->authenticationProtocolDesignation, + $this->createdAt + ); + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(4, $resultArray); + + // Simulate a change in user attributes + } + + public function testGetActivityThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getActivity($this->userIdentifierHash, 10, 0); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanDeleteAuthenticationEventsOlderThan(): void + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + $userVersionId = (int)$userVersionResult[VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $this->repository->insertAuthenticationEvent( + $spId, + $userVersionId, + $this->createdAt, + $this->clientIpAddress, + $this->authenticationProtocolDesignation, + $this->createdAt + ); + + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(1, $resultArray); + + $dateTimeInFuture = $this->createdAt->add(new DateInterval('P1D')); + + $this->repository->deleteAuthenticationEventsOlderThan($dateTimeInFuture); + + $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0); + $this->assertCount(0, $resultArray); + } + + public function testDeleteAuthenticationEventsOlderThanThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->deleteAuthenticationEventsOlderThan(new DateTimeImmutable()); + } +} diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/StoreTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/StoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..65c81230fa50573a7bb8cd8bea1442139b4793b3 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Current/StoreTest.php @@ -0,0 +1,325 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current; + +use DateTimeImmutable; +use Doctrine\DBAL\Result; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\RawRowResult; +use SimpleSAML\Test\Module\accounting\Constants\StateArrays; +use SimpleSAML\Module\accounting\Entities\Authentication\Event\State; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedTableConstants; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event + * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2 + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000200CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000400CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000500CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Migrations\Version20220801000700CreateAuthenticationEventTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + * @uses \SimpleSAML\Module\accounting\Helpers\Hash + * @uses \SimpleSAML\Module\accounting\Helpers\Arr + * @uses \SimpleSAML\Module\accounting\Helpers\Network + * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag + * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider + * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService + * @uses \SimpleSAML\Module\accounting\Entities\User + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity + * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag + * @uses \SimpleSAML\Module\accounting\Entities\Activity + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\RawActivity + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2 + * @uses \SimpleSAML\Module\accounting\Helpers\ProviderResolver + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2 + */ +class StoreTest extends TestCase +{ + protected Stub $moduleConfigurationStub; + protected Migrator $migrator; + protected Stub $factoryStub; + protected Connection $connection; + protected State\Saml2 $state; + protected Event $authenticationEvent; + protected HashDecoratedState $hashDecoratedState; + /** + * @var MockObject + */ + protected $repositoryMock; + /** + * @var Stub + */ + protected $resultStub; + /** + * @var MockObject + */ + protected $loggerMock; + /** + * @var MockObject + */ + protected $helpersManagerMock; + + /** + * @throws StoreException + */ + protected function setUp(): void + { + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class); + $this->moduleConfigurationStub->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->moduleConfigurationStub->method('getUserIdAttributeName') + ->willReturn('hrEduPersonPersistentID'); + + $this->connection = new Connection($connectionParams); + + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->migrator = new Migrator($this->connection, $this->loggerMock); + + $this->factoryStub = $this->createStub(Factory::class); + $this->factoryStub->method('buildConnection')->willReturn($this->connection); + $this->factoryStub->method('buildMigrator')->willReturn($this->migrator); + + $this->state = new State\Saml2(StateArrays::SAML2_FULL); + $this->authenticationEvent = new Event($this->state); + + $this->hashDecoratedState = new HashDecoratedState($this->state); + $this->repositoryMock = $this->createMock( + Store\Repository::class + ); + + $this->resultStub = $this->createStub(Result::class); + $this->helpersManagerMock = $this->createMock(HelpersManager::class); + } + + /** + * @throws StoreException + */ + public function testCanConstructInstance(): void + { + $this->assertInstanceOf( + Store::class, + new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ) + ); + } + + /** + * @throws StoreException + */ + public function testCanBuildInstance(): void + { + $this->assertInstanceOf( + Store::class, + Store::build($this->moduleConfigurationStub, $this->loggerMock) + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + * @throws MigrationException + */ + public function testCanPersistAuthenticationEvent(): void + { + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ); + $store->runSetup(); + + $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $authenticationEventCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $spCountQueryBuilder->select('COUNT(id) as spCount')->from( + //'vds_sp' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP + ) + ); + $userCountQueryBuilder->select('COUNT(id) as userCount')->from( + //'vds_user' + $this->connection->preparePrefixedTableName( + VersionedTableConstants::TABLE_PREFIX . VersionedTableConstants::TABLE_NAME_USER + ) + ); + $userVersionCountQueryBuilder->select('COUNT(id) as userVersionCount')->from( + //'vds_user_version' + $this->connection->preparePrefixedTableName( + VersionedTableConstants::TABLE_PREFIX . VersionedTableConstants::TABLE_NAME_USER_VERSION + ) + ); + $authenticationEventCountQueryBuilder->select('COUNT(id) as authenticationEventCount') + ->from( + //'vds_authentication_event' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_AUTHENTICATION_EVENT + ) + ); + + $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(2, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne()); + } + + /** + * @throws StoreException + */ + public function testGetActivityReturnsEmptyBagIfNoResults(): void + { + $this->repositoryMock->method('getActivity')->willReturn([]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $activityBag = $store->getActivity('test', 10, 0); + + $this->assertEmpty($activityBag->getAll()); + } + + /** + * @throws StoreException + */ + public function testCanGetActivityBag(): void + { + $this->repositoryMock->method('getActivity') + ->willReturn([RawRowResult::ACTIVITY]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $activityBag = $store->getActivity('test', 10, 0); + + $this->assertNotEmpty($activityBag->getAll()); + } + + /** + * @throws StoreException + */ + public function testGetActivityThrowsForInvalidResult(): void + { + $rawResult = RawRowResult::ACTIVITY; + unset($rawResult[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]); + + $this->repositoryMock->method('getActivity') + ->willReturn([$rawResult]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + $store->getActivity('test', 10, 0); + } + + /** + * @throws StoreException + */ + public function testCanDeleteDataOlderThan(): void + { + $dateTime = new DateTimeImmutable(); + + $this->repositoryMock->expects($this->once()) + ->method('deleteAuthenticationEventsOlderThan') + ->with($dateTime); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $store->deleteDataOlderThan($dateTime); + } +} diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivityTest.php old mode 100644 new mode 100755 similarity index 75% rename from tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php rename to tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivityTest.php index 73f332287d4c4a83a5b22bb8fb6f7394a86a4896..a406d53fb62530be7d62ab23938aa4e87e81a774 --- a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php +++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/RawActivityTest.php @@ -2,20 +2,21 @@ declare(strict_types=1); -namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store; +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal; use DateTimeImmutable; use Doctrine\DBAL\Platforms\AbstractPlatform; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\RawActivity; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants; use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2; use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; use SimpleSAML\Test\Module\accounting\Constants\DateTime; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants; /** - * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\RawActivity * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity */ class RawActivityTest extends TestCase @@ -47,15 +48,16 @@ class RawActivityTest extends TestCase $this->authenticationProtocolDesignation = Saml2::DESIGNATION; $this->rawRow = [ - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => serialize($this->serviceProviderMetadata), - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => serialize($this->userAttributes), - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => $this->happenedAt, - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => $this->clientIpAddress, - TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => serialize($this->serviceProviderMetadata), + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => serialize($this->userAttributes), + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => $this->happenedAt, + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => $this->clientIpAddress, + EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => $this->authenticationProtocolDesignation, ]; $this->abstractPlatformStub = $this->createStub(AbstractPlatform::class); - $this->abstractPlatformStub->method('getDateTimeFormatString')->willReturn(DateTime::DEFAULT_FORMAT); + $this->abstractPlatformStub->method('getDateTimeFormatString') + ->willReturn(DateTime::DEFAULT_FORMAT); } public function testCanCreateInstance(): void @@ -85,7 +87,7 @@ class RawActivityTest extends TestCase public function testIpAddressCanBeNull(): void { $rawRow = $this->rawRow; - unset($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]); + unset($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS]); $rawActivity = new RawActivity($rawRow, $this->abstractPlatformStub); $this->assertNull($rawActivity->getClientIpAddress()); @@ -94,7 +96,7 @@ class RawActivityTest extends TestCase public function testAuthenticationProtocolDesignationCanBeNull(): void { $rawRow = $this->rawRow; - unset($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]); + unset($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION]); $rawActivity = new RawActivity($rawRow, $this->abstractPlatformStub); $this->assertNull($rawActivity->getAuthenticationProtocolDesignation()); @@ -103,7 +105,7 @@ class RawActivityTest extends TestCase public function testThrowsIfColumnNotPresent(): void { $rawRow = $this->rawRow; - unset($rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]); + unset($rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]); $this->expectException(UnexpectedValueException::class); @@ -113,7 +115,7 @@ class RawActivityTest extends TestCase public function testThrowsForNonStringServiceProviderMetadata(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] = 1; + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] = 1; $this->expectException(UnexpectedValueException::class); @@ -123,7 +125,7 @@ class RawActivityTest extends TestCase public function testThrowsForNonStringUserAttributes(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] = 1; + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] = 1; $this->expectException(UnexpectedValueException::class); @@ -133,7 +135,7 @@ class RawActivityTest extends TestCase public function testThrowsForNonStringHappenedAt(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT] = 1; + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT] = 1; $this->expectException(UnexpectedValueException::class); @@ -143,7 +145,7 @@ class RawActivityTest extends TestCase public function testThrowsForInvalidServiceProviderMetadata(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] = serialize(1); + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA] = serialize(1); $this->expectException(UnexpectedValueException::class); @@ -153,7 +155,7 @@ class RawActivityTest extends TestCase public function testThrowsForInvalidUserAttributes(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1); + $rawRow[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1); $this->expectException(UnexpectedValueException::class); diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php old mode 100644 new mode 100755 index d86650ef30a2ef7892dbd7f1d904350f8a359e0e..de5ba8527963d0d921cd9b92334a1bc4d70a2a67 --- a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php +++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php @@ -13,8 +13,8 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants - as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as BaseTableConstants; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; @@ -28,6 +28,7 @@ use SimpleSAML\Module\accounting\Services\HelpersManager; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; use SimpleSAML\Test\Module\accounting\Constants\RawRowResult; use SimpleSAML\Test\Module\accounting\Constants\StateArrays; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\EntityTableConstants; /** * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store @@ -71,7 +72,7 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays; * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag * @uses \SimpleSAML\Module\accounting\Entities\Activity - * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\RawActivity * @uses \SimpleSAML\Module\accounting\Services\HelpersManager * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2 @@ -371,7 +372,7 @@ class StoreTest extends TestCase public function testGetActivityThrowsForInvalidResult(): void { $rawResult = RawRowResult::ACTIVITY; - unset($rawResult[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]); + unset($rawResult[EntityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]); $this->repositoryMock->method('getActivity') ->willReturn([$rawResult]); diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a8c5cb183b1c9b8b0ff49194db2ac89e42d60f2d --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTableTest.php @@ -0,0 +1,88 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateIdpTable; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateIdpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + */ +class CreateIdpTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + protected function setUp(): void + { + + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'cds_idp'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws Exception + * @throws MigrationException + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new CreateIdpTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } + + /** + * @throws StoreException + */ + public function testRunThrowsMigrationException(): void + { + $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName); + $this->schemaManagerStub->method('createTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new CreateIdpTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } + + /** + * @throws StoreException + */ + public function testRevertThrowsMigrationException(): void + { + $this->schemaManagerStub->method('dropTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new CreateIdpTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->revert(); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7993200725d2bc5e66d549caee2c5cf1e550c31d --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTableTest.php @@ -0,0 +1,103 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + */ +class CreateSpTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'cds_sp'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws Exception + * @throws MigrationException + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new CreateSpTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } + + /** + * @throws StoreException + */ + public function testRunThrowsMigrationException(): void + { + $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName); + $this->schemaManagerStub->method('createTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new CreateSpTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } + + /** + * @throws StoreException + */ + public function testRevertThrowsMigrationException(): void + { + $this->schemaManagerStub->method('dropTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new CreateSpTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->revert(); + } + + /** + * @throws StoreException + */ + public function testRunThrowsOnIvalidTableNameIdp(): void + { + $this->connectionStub->method('preparePrefixedTableName') + ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table + + $migration = new CreateSpTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b42a7bd8361d1620f6d2a4ac63385abe101051af --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTableTest.php @@ -0,0 +1,60 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + */ +class CreateUserTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'vds_user'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws MigrationException + * @throws Exception + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new CreateUserTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f34201a55ae2ed91c750373726e2c32ced253e74 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTableTest.php @@ -0,0 +1,58 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + */ +class CreateUserVersionTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'vds_user_version'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws MigrationException + * @throws Exception + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new Migrations\CreateUserVersionTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/RepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7db77150a1b15c280d8423e3a4c4367c738d2819 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/RepositoryTest.php @@ -0,0 +1,390 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store; + +use DateTimeImmutable; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedTableConstants; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\DateTime; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + * @uses \SimpleSAML\Module\accounting\ModuleConfiguration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateIdpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + */ +class RepositoryTest extends TestCase +{ + protected Connection $connection; + protected \Doctrine\DBAL\Connection $dbal; + protected Stub $loggerStub; + protected Migrator $migrator; + protected string $dateTimeFormat; + protected string $idpEntityId; + protected string $idpEntityIdHash; + protected string $idpMetadata; + protected string $idpMetadataHash; + protected string $spEntityId; + protected string $spMetadataHash; + protected string $userIdentifier; + protected string $userIdentifierHash; + protected string $userAttributes; + protected string $userAttributesHash; + protected Repository $repository; + protected DateTimeImmutable $createdAt; + protected Stub $connectionStub; + protected string $spEntityIdHash; + protected string $spMetadata; + protected string $clientIpAddress; + protected string $authenticationProtocolDesignation; + + /** + * @throws StoreException + * @throws MigrationException + */ + protected function setUp(): void + { + // For stubbing. + $this->connectionStub = $this->createStub(Connection::class); + $this->loggerStub = $this->createStub(LoggerInterface::class); + + // For real DB testing. + $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->connection = new Connection($connectionParameters); + $this->migrator = new Migrator($this->connection, $this->loggerStub); + $moduleConfiguration = new ModuleConfiguration(); + $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR . + 'Data' . DIRECTORY_SEPARATOR . + 'Stores' . DIRECTORY_SEPARATOR . + 'Accounting' . DIRECTORY_SEPARATOR . + 'Bases' . DIRECTORY_SEPARATOR . + 'DoctrineDbal' . DIRECTORY_SEPARATOR . + 'Current' . DIRECTORY_SEPARATOR . + 'Store' . DIRECTORY_SEPARATOR . + AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + + $this->migrator->runSetup(); + $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace); + + $this->repository = new Repository($this->connection, $this->loggerStub); + + $this->dateTimeFormat = DateTime::DEFAULT_FORMAT; + + $this->idpEntityId = 'idp-entity-id'; + $this->idpEntityIdHash = 'idp-entity-id-hash'; + + $this->idpMetadata = 'idp-metadata'; + $this->idpMetadataHash = 'idp-metadata-hash'; + + $this->spEntityId = 'sp-entity-id'; + $this->spEntityIdHash = 'sp-entity-id-hash'; + + $this->spMetadata = 'sp-metadata'; + $this->spMetadataHash = 'sp-metadata-hash'; + + $this->userIdentifier = 'user-identifier'; + $this->userIdentifierHash = 'user-identifier-hash'; + + $this->userAttributes = 'user-attributes'; + $this->userAttributesHash = 'user-attributes-hash'; + + $this->createdAt = new DateTimeImmutable(); + $this->clientIpAddress = '123.123.123.123'; + $this->authenticationProtocolDesignation = Saml2::DESIGNATION; + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + Repository::class, + new Repository($this->connection, $this->loggerStub) + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanInsertAndGetIdp(): array + { + $this->repository->insertIdp( + $this->idpEntityId, + $this->idpEntityIdHash, + $this->idpMetadata, + $this->idpMetadataHash, + $this->createdAt + ); + + $result = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative(); + + $this->assertSame($this->idpEntityId, $result[TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID]); + $this->assertSame( + $this->idpEntityIdHash, + $result[TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256] + ); + $this->assertSame($this->idpMetadata, $result[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA]); + $this->assertSame($this->idpMetadataHash, $result[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256]); + $this->assertSame( + $this->createdAt->format($this->dateTimeFormat), + $result[TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT] + ); + + return $result; + } + + public function testInsertIdpThrowsOnNonUniqueIdpEntityIdHash(): void + { + $this->expectException(StoreException::class); + + // Can't insert duplicate idp entity ID hash. + $this->repository->insertIdp( + $this->idpEntityId, + $this->idpEntityIdHash, + $this->idpMetadata, + $this->idpMetadataHash, + $this->createdAt + ); + $this->repository->insertIdp( + $this->idpEntityId, + $this->idpEntityIdHash, + $this->idpMetadata, + $this->idpMetadataHash, + $this->createdAt + ); + } + + public function testGetIdpThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getIdp($this->idpEntityIdHash); + } + + /** + * @throws StoreException + */ + public function testCanUpdateIdp(): void + { + $this->repository->insertIdp( + $this->idpEntityId, + $this->idpEntityIdHash, + $this->idpMetadata, + $this->idpMetadataHash, + $this->createdAt + ); + + $result = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative(); + $this->assertSame($this->idpMetadata, $result[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA]); + + $this->repository->updateIdp( + $result[TableConstants::TABLE_IDP_COLUMN_NAME_ID], + 'new-idp-metadata', + 'new-idp-metadata-hash' + ); + + $newResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative(); + $this->assertSame('new-idp-metadata', $newResult[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA]); + $this->assertSame( + 'new-idp-metadata-hash', + $newResult[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256] + ); + + $this->assertNotSame( + $result[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA], + $newResult[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA] + ); + $this->assertNotSame( + $this->idpMetadata, + $newResult[TableConstants::TABLE_IDP_COLUMN_NAME_METADATA] + ); + } + + public function testUpdateIdpThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->updateIdp( + 1, + 'new-idp-metadata', + 'new-idp-metadata-hash' + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanInsertAndGetSp(): array + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + + $result = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + + $this->assertSame($this->spEntityId, $result[TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID]); + $this->assertSame( + $this->spEntityIdHash, + $result[TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256] + ); + $this->assertSame($this->spMetadata, $result[TableConstants::TABLE_SP_COLUMN_NAME_METADATA]); + $this->assertSame($this->spMetadataHash, $result[TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256]); + $this->assertSame( + $this->createdAt->format($this->dateTimeFormat), + $result[TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT] + ); + + return $result; + } + + public function testInsertSpThrowsOnNonUniqueSpEntityIdHash(): void + { + $this->expectException(StoreException::class); + // SP Entity ID Hash must be unique. + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + } + + public function testGetSpThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getSp($this->spEntityIdHash); + } + + public function testCanUpdateSp(): void + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + + $result = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $this->assertSame($this->spMetadata, $result[TableConstants::TABLE_SP_COLUMN_NAME_METADATA]); + + $this->repository->updateSp( + $result[TableConstants::TABLE_SP_COLUMN_NAME_ID], + 'new-sp-metadata', + 'new-sp-metadata-hash' + ); + + $newResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $this->assertSame('new-sp-metadata', $newResult[TableConstants::TABLE_SP_COLUMN_NAME_METADATA]); + $this->assertSame( + 'new-sp-metadata-hash', + $newResult[TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256] + ); + + $this->assertNotSame( + $result[TableConstants::TABLE_SP_COLUMN_NAME_METADATA], + $newResult[TableConstants::TABLE_SP_COLUMN_NAME_METADATA] + ); + $this->assertNotSame( + $this->spMetadata, + $newResult[TableConstants::TABLE_SP_COLUMN_NAME_METADATA] + ); + } + + public function testUpdateSpThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->updateSp( + 1, + 'new-sp-metadata', + 'new-sp-metadata-hash' + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanInsertAndGetUser(): array + { + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + + $result = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + + $this->assertSame($this->userIdentifier, $result[VersionedTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER]); + $this->assertSame( + $this->userIdentifierHash, + $result[VersionedTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256] + ); + $this->assertSame( + $this->createdAt->format($this->dateTimeFormat), + $result[VersionedTableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT] + ); + + return $result; + } + + public function testInsertUserThrowsOnNonUniqueIdentifierHash(): void + { + $this->expectException(StoreException::class); + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + } + + public function testGetUserThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getUser($this->userIdentifierHash); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/StoreTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/StoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8200383dffd5aa09d0391adc6c525d3c15ff5404 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/StoreTest.php @@ -0,0 +1,226 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current; + +use Doctrine\DBAL\Result; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\StateArrays; +use SimpleSAML\Module\accounting\Entities\Authentication\Event\State; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2 + * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState + * @uses \SimpleSAML\Module\accounting\Helpers\Arr + * @uses \SimpleSAML\Module\accounting\Helpers\Hash + * @uses \SimpleSAML\Module\accounting\Helpers\Network + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + * @uses \SimpleSAML\Module\accounting\Traits\HasUserAttributesTrait + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateIdpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + */ +class StoreTest extends TestCase +{ + protected Stub $moduleConfigurationStub; + protected Migrator $migrator; + protected Stub $factoryStub; + protected Connection $connection; + protected State\Saml2 $state; + protected Event $authenticationEvent; + protected HashDecoratedState $hashDecoratedState; + protected MockObject $repositoryMock; + protected Stub $resultStub; + protected MockObject $loggerMock; + protected MockObject $helpersManagerMock; + + /** + * @throws StoreException + */ + protected function setUp(): void + { + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class); + $this->moduleConfigurationStub->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->moduleConfigurationStub->method('getUserIdAttributeName') + ->willReturn('hrEduPersonPersistentID'); + + $this->connection = new Connection($connectionParams); + + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->migrator = new Migrator($this->connection, $this->loggerMock); + + $this->factoryStub = $this->createStub(Factory::class); + $this->factoryStub->method('buildConnection')->willReturn($this->connection); + $this->factoryStub->method('buildMigrator')->willReturn($this->migrator); + + $this->state = new State\Saml2(StateArrays::SAML2_FULL); + $this->authenticationEvent = new Event($this->state); + + $this->hashDecoratedState = new HashDecoratedState($this->state); + $this->repositoryMock = $this->createMock(Repository::class); + + $this->resultStub = $this->createStub(Result::class); + $this->helpersManagerMock = $this->createMock(HelpersManager::class); + } + + /** + * @throws StoreException + */ + public function testCanConstructInstance(): void + { + $this->assertInstanceOf( + Store::class, + new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ) + ); + } + + /** + * @throws StoreException + */ + public function testCanBuildInstance(): void + { + $this->assertInstanceOf( + Store::class, + Store::build($this->moduleConfigurationStub, $this->loggerMock) + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + * @throws MigrationException + */ + public function testCanResolveSpId(): void + { + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ); + $store->runSetup(); + + $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $spCountQueryBuilder->select('COUNT(id) as spCount')->from( + $this->connection->preparePrefixedTableName( + TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_SP + ) + ); + + $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + + $spId = $store->resolveSpId($this->hashDecoratedState); + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + + $newSpId = $store->resolveSpId($this->hashDecoratedState); + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame($spId, $newSpId); + + // Test updated metadata + $updatedStateArray = StateArrays::SAML2_FULL; + $updatedStateArray['SPMetadata']['name'] = 'Updated name'; + $updatedState = new State\Saml2($updatedStateArray); + $updatedHasDecoratedState = new HashDecoratedState($updatedState); + $newSpId = $store->resolveSpId($updatedHasDecoratedState); + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame($spId, $newSpId); + + // Test new SP + $newStateArray = StateArrays::SAML2_FULL; + $newStateArray['SPMetadata']['entityid'] = 'new-entity-id'; + $newState = new State\Saml2($newStateArray); + $newHasDecoratedState = new HashDecoratedState($newState); + $newSpId = $store->resolveSpId($newHasDecoratedState); + $this->assertSame(2, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertNotSame($spId, $newSpId); + } + + /** + * @throws StoreException + */ + public function testResolveSpIdThrowsOnFirstGetSpFailure(): void + { + $this->repositoryMock->method('getSp')->willThrowException(new Exception('test')); + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + + $store->resolveSpId($this->hashDecoratedState); + } + + /** + * @throws StoreException + */ + public function testResolveSpIdThrowsOnInsertAndLogsWarning(): void + { + $this->resultStub->method('fetchAssociative')->willReturn(false); + $this->resultStub->method('fetchOne')->willReturn(false); + $this->repositoryMock->method('getSp')->willReturn($this->resultStub); + $this->repositoryMock->method('insertSp')->willThrowException(new Exception('test')); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + $this->loggerMock->expects($this->once())->method('warning'); + + $store->resolveSpId($this->hashDecoratedState); + } +} diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php old mode 100644 new mode 100755 index cba763ee83600ed71f2ea590c97f92b932f71903..87e694e8ff5527c3da2f99d4f93a835007a48b59 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php @@ -53,6 +53,8 @@ class CreateIdpSpUserVersionTableTest extends TestCase $migration = new Migrations\CreateIdpSpUserVersionTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php old mode 100644 new mode 100755 index 41792f86f6861354ed66e07c1ac2a551dc0b77f3..baa3fffe6f726ccc9a5cdc9b1def1b5193bae189 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php @@ -53,6 +53,8 @@ class CreateIdpTableTest extends TestCase $migration = new CreateIdpTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php old mode 100644 new mode 100755 index cc8c07955ca5c3cf20baf32023b0d63a33e729ef..a135b62e95f30bbe9fa5f673cea8c80457502f04 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php @@ -53,6 +53,8 @@ class CreateIdpVersionTableTest extends TestCase $migration = new Migrations\CreateIdpVersionTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php old mode 100644 new mode 100755 index a388427bcce0c087d1475f9f9e7edf861c9565a5..05d9c7dda6f241561d8b705f4f6e2fe308d6dae0 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php @@ -53,6 +53,8 @@ class CreateSpTableTest extends TestCase $migration = new CreateSpTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php old mode 100644 new mode 100755 index 44e4151b76452401805973642318f4247631f528..2bf6767580b1db4efdfdb19fc326df5565fd07ed --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php @@ -53,6 +53,8 @@ class CreateSpVersionTableTest extends TestCase $migration = new Migrations\CreateSpVersionTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php old mode 100644 new mode 100755 index 786ff8bf0450b059855cd3929d23bbc755e734ae..4d83671ac55d4a230c663e7935662822e553a5a5 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php @@ -15,8 +15,7 @@ use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; /** - * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable - * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection */ @@ -54,6 +53,8 @@ class CreateUserTableTest extends TestCase $migration = new CreateUserTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php old mode 100644 new mode 100755 index a1515049e9d08b38455305ffae22c037609f9b56..feef85d439e818bcb3dd297e27c7633b0d813f72 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php @@ -53,6 +53,8 @@ class CreateUserVersionTableTest extends TestCase $migration = new Migrations\CreateUserVersionTable($this->connection); $migration->run(); $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); $migration->revert(); $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php old mode 100644 new mode 100755 index 2007a264ba492429d3c84296034793adfe1f0adb..91edd1c4bb0fd0bf17904b41bc7c5c86ef847586 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php @@ -391,4 +391,98 @@ class RepositoryTest extends TestCase $repository->getUserVersion(1, $this->userIdentifierHash); } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanInsertAndGetIdpSpUserVersion(): void + { + $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt); + $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative(); + $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID]; + $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt); + $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative(); + + $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt); + $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative(); + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + + $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID]; + $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID]; + $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt); + $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId) + ->fetchAssociative(); + + $this->assertSame( + $idpVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID] + ); + $this->assertSame( + $spVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID] + ); + $this->assertSame( + $userVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID] + ); + + // Simulate another SP + $spEntityIdNew = $this->spEntityId . '-new'; + $spEntityIdHashNew = $this->spEntityIdHash . '-new'; + $spMetadataNew = $this->spMetadata . '-new'; + $spMetadataHashNew = $this->spMetadataHash . '-new'; + $this->repository->insertSp($spEntityIdNew, $spEntityIdHashNew, $this->createdAt); + $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + $this->repository->insertSpVersion($spId, $spMetadataNew, $spMetadataHashNew, $this->createdAt); + $spVersionResult = $this->repository->getSpVersion($spId, $spMetadataHashNew)->fetchAssociative(); + $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID]; + + $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt); + $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId) + ->fetchAssociative(); + + $this->assertSame( + $idpVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID] + ); + $this->assertSame( + $spVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID] + ); + $this->assertSame( + $userVersionId, + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID] + ); + } + + public function testGetIdpSpUserVersionThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getIdpSpUserVersion(1, 1, 1); + } + + public function testInsertIdpSpUserVersionThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->insertIdpSpUserVersion(1, 1, 1); + } } diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php old mode 100644 new mode 100755 index 10c9068482d3bea1fb5755285ead9c118cba32fa..281456c72d49e5ffa04de2e572c44898fc75da41 --- a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php +++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php @@ -5,12 +5,12 @@ declare(strict_types=1); namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned; use Doctrine\DBAL\Result; -use Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants; use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; @@ -18,6 +18,7 @@ use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; use SimpleSAML\Module\accounting\Entities\Authentication\Event; use SimpleSAML\Module\accounting\Entities\Authentication\Event\State; +use SimpleSAML\Module\accounting\Exceptions\Exception; use SimpleSAML\Module\accounting\Exceptions\StoreException; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; @@ -113,9 +114,7 @@ class StoreTest extends TestCase $this->authenticationEvent = new Event($this->state); $this->hashDecoratedState = new HashDecoratedState($this->state); - $this->repositoryMock = $this->createMock( - \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository::class - ); + $this->repositoryMock = $this->createMock(Repository::class); $this->resultStub = $this->createStub(Result::class); $this->helpersManagerMock = $this->createMock(HelpersManager::class); @@ -170,6 +169,7 @@ class StoreTest extends TestCase $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); $spVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $idpSpUserVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); $idpCountQueryBuilder->select('COUNT(id) as idpCount')->from( @@ -208,40 +208,52 @@ class StoreTest extends TestCase TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_USER_VERSION ) ); + $idpSpUserVersionCountQueryBuilder->select('COUNT(id) as idpSpUserVersionCount') + ->from( + //'vds_idp_sp_user_version' + $this->connection->preparePrefixedTableName( + TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_IDP_SP_USER_VERSION + ) + ); $this->assertSame(0, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(0, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(0, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); $idpId = $store->resolveIdpId($this->hashDecoratedState); - $store->resolveIdpVersionId($idpId, $this->hashDecoratedState); + $idpVersionId = $store->resolveIdpVersionId($idpId, $this->hashDecoratedState); $spId = $store->resolveSpId($this->hashDecoratedState); - $store->resolveSpVersionId($spId, $this->hashDecoratedState); + $spVersionId = $store->resolveSpVersionId($spId, $this->hashDecoratedState); $userId = $store->resolveUserId($this->hashDecoratedState); - $store->resolveUserVersionId($userId, $this->hashDecoratedState); + $userVersionId = $store->resolveUserVersionId($userId, $this->hashDecoratedState); + $store->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId); $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); $idpId = $store->resolveIdpId($this->hashDecoratedState); - $store->resolveIdpVersionId($idpId, $this->hashDecoratedState); + $idpVersionId = $store->resolveIdpVersionId($idpId, $this->hashDecoratedState); $spId = $store->resolveSpId($this->hashDecoratedState); - $store->resolveSpVersionId($spId, $this->hashDecoratedState); + $spVersionId = $store->resolveSpVersionId($spId, $this->hashDecoratedState); $userId = $store->resolveUserId($this->hashDecoratedState); - $store->resolveUserVersionId($userId, $this->hashDecoratedState); + $userVersionId = $store->resolveUserVersionId($userId, $this->hashDecoratedState); + $store->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId); $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); } @@ -549,4 +561,51 @@ class StoreTest extends TestCase $userId = $store->resolveUserId($this->hashDecoratedState); $store->resolveUserVersionId($userId, $this->hashDecoratedState); } + + /** + * @throws StoreException + */ + public function testResolveIdpSpUserVersionThrowsOnGet(): void + { + $this->repositoryMock->method('getIdpSpUserVersion')->willThrowException(new Exception('test')); + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + + $store->resolveIdpSpUserVersionId(1, 1, 1); + } + + + + /** + * @throws StoreException + */ + public function testResolveIdpSpUserVersionLogsWarningAndThrowsOnFailure(): void + { + $this->resultStub->method('fetchOne')->willReturn(false); + $this->repositoryMock->method('getIdpSpUserVersion')->willReturn($this->resultStub); + $this->repositoryMock->method('insertIdpSpUserVersion') + ->willThrowException(new Exception('test')); + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + $this->loggerMock->expects($this->once())->method('warning'); + $store->resolveIdpSpUserVersionId(1, 1, 1); + } } diff --git a/tests/src/Data/Stores/Accounting/Bases/HashDecoratedStateTest.php b/tests/src/Data/Stores/Accounting/Bases/HashDecoratedStateTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTableTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f33a81d470a68200dc620ab827b8bdebf1894eab --- /dev/null +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTableTest.php @@ -0,0 +1,102 @@ +<?php + +// phpcs:ignore +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations\Version20240505400CreateConnectedServiceTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + */ +class Version20240505400CreateConnectedServiceTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'cds_connected_service'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws MigrationException + * @throws Exception + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new Migrations\Version20240505400CreateConnectedServiceTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } + + /** + * @throws StoreException + */ + public function testRunThrowsMigrationException(): void + { + $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName); + $this->schemaManagerStub->method('createTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Migrations\Version20240505400CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } + + /** + * @throws StoreException + */ + public function testRevertThrowsMigrationException(): void + { + $this->schemaManagerStub->method('dropTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Migrations\Version20240505400CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->revert(); + } + + /** + * @throws StoreException + */ + public function testRunThrowsOnIvalidTableName(): void + { + $this->connectionStub->method('preparePrefixedTableName') + ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table + + $migration = new Migrations\Version20240505400CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } +} diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/RepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3b485d74b1292c303698ee9f15992fe166cbb93b --- /dev/null +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/RepositoryTest.php @@ -0,0 +1,363 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store; + +use DateInterval; +use DateTimeImmutable; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\DateTime; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedBaseTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations\Version20240505400CreateConnectedServiceTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + * @uses \SimpleSAML\Module\accounting\ModuleConfiguration + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + * + */ +class RepositoryTest extends TestCase +{ + protected Connection $connection; + protected \Doctrine\DBAL\Connection $dbal; + protected Stub $loggerStub; + protected Migrator $migrator; + protected string $dateTimeFormat; + protected string $idpEntityId; + protected string $idpEntityIdHash; + protected string $idpMetadata; + protected string $idpMetadataHash; + protected string $spEntityId; + protected string $spMetadataHash; + protected string $userIdentifier; + protected string $userIdentifierHash; + protected string $userAttributes; + protected string $userAttributesHash; + protected Repository $repository; + protected DateTimeImmutable $createdAt; + protected Stub $connectionStub; + protected string $spEntityIdHash; + protected string $spMetadata; + + /** + * @throws StoreException + * @throws MigrationException + */ + protected function setUp(): void + { + // For stubbing. + $this->connectionStub = $this->createStub(Connection::class); + $this->loggerStub = $this->createStub(LoggerInterface::class); + + // For real DB testing. + $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->connection = new Connection($connectionParameters); + $this->migrator = new Migrator($this->connection, $this->loggerStub); + $moduleConfiguration = new ModuleConfiguration(); + $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR . + 'Data' . DIRECTORY_SEPARATOR . + 'Stores' . DIRECTORY_SEPARATOR . + 'Accounting' . DIRECTORY_SEPARATOR . + 'ConnectedServices' . DIRECTORY_SEPARATOR . + 'DoctrineDbal' . DIRECTORY_SEPARATOR . + 'Current' . DIRECTORY_SEPARATOR . + 'Store' . DIRECTORY_SEPARATOR . + AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME; + + $this->migrator->runSetup(); + $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace); + + $this->repository = new Repository($this->connection, $this->loggerStub); + + $this->dateTimeFormat = DateTime::DEFAULT_FORMAT; + + $this->idpEntityId = 'idp-entity-id'; + $this->idpEntityIdHash = 'idp-entity-id-hash'; + + $this->idpMetadata = 'idp-metadata'; + $this->idpMetadataHash = 'idp-metadata-hash'; + + $this->spEntityId = 'sp-entity-id'; + $this->spEntityIdHash = 'sp-entity-id-hash'; + + $this->spMetadata = 'sp-metadata'; + $this->spMetadataHash = 'sp-metadata-hash'; + + $this->userIdentifier = 'user-identifier'; + $this->userIdentifierHash = 'user-identifier-hash'; + + $this->userAttributes = 'user-attributes'; + $this->userAttributesHash = 'user-attributes-hash'; + + $this->createdAt = new DateTimeImmutable(); + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + Repository::class, + new Repository($this->connection, $this->loggerStub) + ); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanGetConnectedServices(): void + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + $userVersionId = (int)$userVersionResult[VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(0, $resultArray); + + $this->repository->insertConnectedService($spId, $userId, $userVersionId); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(1, $resultArray); + + $this->assertEquals( + '1', + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + ); + $this->assertSame( + $this->spMetadata, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + ); + $this->assertSame( + $this->userAttributes, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + ); + + $connectedServiceId = (int)$this->repository->getConnectedService($spId, $userId)->fetchOne(); + + $this->repository->updateConnectedServiceVersionCount( + $connectedServiceId, + $userVersionId, + new DateTimeImmutable() + ); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(1, $resultArray); + $this->assertEquals( + '2', + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + ); + $this->assertSame( + $this->spMetadata, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + ); + $this->assertSame( + $this->userAttributes, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + ); + + // Simulate another SP + $spEntityIdNew = $this->spEntityId . '-new'; + $spEntityIdHashNew = $this->spEntityIdHash . '-new'; + $spMetadataNew = $this->spMetadata . '-new'; + $spMetadataHashNew = $this->spMetadataHash . '-new'; + $this->repository->insertSp( + $spEntityIdNew, + $spEntityIdHashNew, + $spMetadataNew, + $spMetadataHashNew, + $this->createdAt + ); + $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertConnectedService($spId, $userId, $userVersionId); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(2, $resultArray); + $this->assertEquals( + '1', + $resultArray[1] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + ); + $this->assertSame( + $spMetadataNew, + $resultArray[1] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + ); + $this->assertSame( + $this->userAttributes, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + ); + + // Simulate change in user attributes + $userAttributesNew = $this->userAttributes . '-new'; + $userAttributesHashNew = $this->userAttributesHash . '-new'; + $this->repository->insertUserVersion($userId, $userAttributesNew, $userAttributesHashNew, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $userAttributesHashNew)->fetchAssociative(); + $userVersionId = (int)$userVersionResult[VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $connectedServiceId = (int)$this->repository->getConnectedService($spId, $userId)->fetchOne(); + + $this->repository->updateConnectedServiceVersionCount( + $connectedServiceId, + $userVersionId, + new DateTimeImmutable() + ); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + + $this->assertCount(2, $resultArray); + $this->assertEquals( + '2', + $resultArray[1] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + ); + $this->assertSame( + $spMetadataNew, + $resultArray[1] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + ); + // New SP with new user attributes version.. + $this->assertSame( + $userAttributesNew, + $resultArray[1] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + ); + + // First SP still has old user attributes version... + $this->assertSame( + $this->userAttributes, + $resultArray[0] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + ); + } + + public function testInsertConnectedServiceThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->insertConnectedService(1, 1, 1); + } + + public function testGetConnectedServiceThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getConnectedService(1, 1); + } + + public function testGetConnectedServicesThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getConnectedServices($this->userIdentifierHash); + } + + public function testUpdateConnectedServiceVersionCountThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->updateConnectedServiceVersionCount(1, 1, new DateTimeImmutable()); + } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + */ + public function testCanDeleteConnectedServicesOlderThan(): void + { + $this->repository->insertSp( + $this->spEntityId, + $this->spEntityIdHash, + $this->spMetadata, + $this->spMetadataHash, + $this->createdAt + ); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[VersionedBaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + $userVersionId = (int)$userVersionResult[VersionedBaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $this->repository->insertConnectedService($spId, $userId, $userVersionId); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(1, $resultArray); + + $dateTimeInFuture = $this->createdAt->add(new DateInterval('P1D')); + + $this->repository->deleteConnectedServicesOlderThan($dateTimeInFuture); + + $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash); + $this->assertCount(0, $resultArray); + } + + public function testDeleteAuthenticationEventsOlderThanThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->deleteConnectedServicesOlderThan(new DateTimeImmutable()); + } +} diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/StoreTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/StoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..16de975ec7ca64e219427c43bad2bc337d786c10 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/StoreTest.php @@ -0,0 +1,325 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current; + +use DateTimeImmutable; +use Doctrine\DBAL\Result; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\TableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Test\Module\accounting\Constants\RawRowResult; +use SimpleSAML\Test\Module\accounting\Constants\StateArrays; +use SimpleSAML\Module\accounting\Entities\Authentication\Event\State; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as VersionedBaseTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event + * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2 + * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState + * @uses \SimpleSAML\Module\accounting\Helpers\Arr + * @uses \SimpleSAML\Module\accounting\Helpers\Hash + * @uses \SimpleSAML\Module\accounting\Services\HelpersManager + * @uses \SimpleSAML\Module\accounting\Helpers\Network + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity + * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService + * @uses \SimpleSAML\Module\accounting\Entities\User + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations\Version20240505400CreateConnectedServiceTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem + */ +class StoreTest extends TestCase +{ + protected Stub $moduleConfigurationStub; + protected Migrator $migrator; + protected Stub $factoryStub; + protected Connection $connection; + protected State\Saml2 $state; + protected Event $authenticationEvent; + protected HashDecoratedState $hashDecoratedState; + /** + * @var MockObject + */ + protected $repositoryMock; + /** + * @var Stub + */ + protected $resultStub; + /** + * @var MockObject + */ + protected $loggerMock; + /** + * @var MockObject + */ + protected $helpersManagerMock; + + /** + * @throws StoreException + */ + protected function setUp(): void + { + $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY; + $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class); + $this->moduleConfigurationStub->method('getConnectionParameters') + ->willReturn($connectionParams); + $this->moduleConfigurationStub->method('getUserIdAttributeName') + ->willReturn('hrEduPersonPersistentID'); + + $this->connection = new Connection($connectionParams); + + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->migrator = new Migrator($this->connection, $this->loggerMock); + + $this->factoryStub = $this->createStub(Factory::class); + $this->factoryStub->method('buildConnection')->willReturn($this->connection); + $this->factoryStub->method('buildMigrator')->willReturn($this->migrator); + + $this->state = new State\Saml2(StateArrays::SAML2_FULL); + $this->authenticationEvent = new Event($this->state); + + $this->hashDecoratedState = new HashDecoratedState($this->state); + $this->repositoryMock = $this->createMock(Repository::class); + + $this->resultStub = $this->createStub(Result::class); + $this->helpersManagerMock = $this->createMock(HelpersManager::class); + } + /** + * @throws StoreException + */ + public function testCanConstructInstance(): void + { + $this->assertInstanceOf( + Store::class, + new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ) + ); + } + + /** + * @throws StoreException + */ + public function testCanBuildInstance(): void + { + $this->assertInstanceOf( + Store::class, + Store::build($this->moduleConfigurationStub, $this->loggerMock) + ); + } + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + * @throws MigrationException + */ + public function testCanPersistAuthenticationEvent(): void + { + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ); + $store->runSetup(); + + $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $connectedServicesQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $spCountQueryBuilder->select('COUNT(id) as spCount')->from( + //'cds_sp' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP + ) + ); + + $userCountQueryBuilder->select('COUNT(id) as userCount')->from( + //'vds_user' + $this->connection->preparePrefixedTableName( + VersionedBaseTableConstants::TABLE_PREFIX . VersionedBaseTableConstants::TABLE_NAME_USER + ) + ); + $userVersionCountQueryBuilder->select('COUNT(id) as userVersionCount')->from( + //'vds_user_version' + $this->connection->preparePrefixedTableName( + VersionedBaseTableConstants::TABLE_PREFIX . VersionedBaseTableConstants::TABLE_NAME_USER_VERSION + ) + ); + + $connectedServicesQueryBuilder->select('COUNT(id) as connectedServiceCount') + ->from( + //'cds_connected_service' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_CONNECTED_SERVICE + ) + ); + + $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$connectedServicesQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$connectedServicesQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$connectedServicesQueryBuilder->executeQuery()->fetchOne()); + + // New SP + $stateArray = StateArrays::SAML2_FULL; + $stateArray['SPMetadata']['entityid'] = 'new-entity-id'; + $state = new State\Saml2($stateArray); + $authenticationEvent = new Event($state); + + $store->persist($authenticationEvent); + + $this->assertSame(2, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(2, (int)$connectedServicesQueryBuilder->executeQuery()->fetchOne()); + } + + /** + * @throws StoreException + */ + public function testGetConnectedOrganizationsReturnsEmptyBagIfNoResults(): void + { + $this->repositoryMock->method('getConnectedServices')->willReturn([]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $connectedServiceProviderBag = $store->getConnectedServices('test'); + + $this->assertEmpty($connectedServiceProviderBag->getAll()); + } + + /** + * @throws StoreException + */ + public function testCanGetConnectedOrganizationsBag(): void + { + $this->repositoryMock->method('getConnectedServices') + ->willReturn([RawRowResult::CONNECTED_SERVICE]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $connectedServiceProviderBag = $store->getConnectedServices('test'); + + $this->assertNotEmpty($connectedServiceProviderBag->getAll()); + } + + /** + * @throws StoreException + */ + public function testGetConnectedOrganizationsThrowsForInvalidResult(): void + { + $rawResult = RawRowResult::CONNECTED_SERVICE; + unset($rawResult[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]); + + $this->repositoryMock->method('getConnectedServices') + ->willReturn([$rawResult]); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $this->expectException(StoreException::class); + $store->getConnectedServices('test'); + } + + /** + * @throws StoreException + */ + public function testCanDeleteDataOlderThan(): void + { + $dateTime = new DateTimeImmutable(); + + $this->repositoryMock->expects($this->once()) + ->method('deleteConnectedServicesOlderThan') + ->with($dateTime); + + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub, + $this->helpersManagerMock, + $this->repositoryMock + ); + + $store->deleteDataOlderThan($dateTime); + } +} diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php old mode 100644 new mode 100755 index a9dc48e2fd26ebd50f97919fb8ac6dfb62376984..c1559de628598defe651ab5ca4f19126c2d5e7d6 --- a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php @@ -12,6 +12,7 @@ use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\Doctri use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants; use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException; use SimpleSAML\Test\Module\accounting\Constants\DateTime; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; /** * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService @@ -45,15 +46,15 @@ class RawConnectedServiceTest extends TestCase $this->serviceProviderMetadata = ['sp' => 'metadata']; $this->userAttributes = ['user' => 'attribute']; $this->rawRow = [ - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS => + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS => $this->numberOfAuthentications, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => $this->lastAuthenticationAt, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => $this->firstAuthenticationAt, - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA => + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA => serialize($this->serviceProviderMetadata), - TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES => + EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES => serialize($this->userAttributes), ]; $this->dateTimeFormat = DateTime::DEFAULT_FORMAT; @@ -95,7 +96,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfColumnNotSet(): void { $rawRow = $this->rawRow; - unset($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]); + unset($rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]); $this->expectException(UnexpectedValueException::class); @@ -105,7 +106,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfNumberOfAuthenticationsNotNumeric(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] = 'a'; + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] = 'a'; $this->expectException(UnexpectedValueException::class); @@ -115,7 +116,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfLastAuthenticationAtNotString(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT] = 1; + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT] = 1; $this->expectException(UnexpectedValueException::class); @@ -125,7 +126,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfFirstAuthenticationAtNotString(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT] = 1; + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT] = 1; $this->expectException(UnexpectedValueException::class); @@ -135,7 +136,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfSpMetadataNotString(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = 1; + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = 1; $this->expectException(UnexpectedValueException::class); @@ -145,7 +146,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfUserAttributesNotString(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = 1; + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = 1; $this->expectException(UnexpectedValueException::class); @@ -155,7 +156,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfSpMetadataNotValid(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = serialize(1); + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = serialize(1); $this->expectException(UnexpectedValueException::class); @@ -165,7 +166,7 @@ class RawConnectedServiceTest extends TestCase public function testThrowsIfUserAttributesNotValid(): void { $rawRow = $this->rawRow; - $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1); + $rawRow[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1); $this->expectException(UnexpectedValueException::class); diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTableTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..21c2ee813d9b81babc023dfca8169edfdd3bc3e9 --- /dev/null +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTableTest.php @@ -0,0 +1,103 @@ +<?php + +//phpcs:ignore +namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use PHPUnit\Framework\MockObject\Stub; +//phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateConnectedServiceTable; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; + +/** + * @covers SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateConnectedServiceTable + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + */ +class Version20220801000700CreateConnectedServiceTableTest extends TestCase +{ + protected Connection $connection; + protected AbstractSchemaManager $schemaManager; + protected string $tableName; + protected Stub $connectionStub; + protected Stub $dbalStub; + protected Stub $schemaManagerStub; + + /** + * @throws Exception + */ + protected function setUp(): void + { + $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->schemaManager = $this->connection->dbal()->createSchemaManager(); + $this->tableName = 'vds_connected_service'; + + $this->connectionStub = $this->createStub(Connection::class); + $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class); + $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class); + } + + /** + * @throws StoreException + * @throws MigrationException + * @throws Exception + */ + public function testCanRunMigration(): void + { + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + $migration = new Version20220801000700CreateConnectedServiceTable($this->connection); + $migration->run(); + $this->assertTrue($this->schemaManager->tablesExist($this->tableName)); + $migration->revert(); + $this->assertFalse($this->schemaManager->tablesExist($this->tableName)); + } + + /** + * @throws StoreException + */ + public function testRunThrowsMigrationException(): void + { + $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName); + $this->schemaManagerStub->method('createTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Version20220801000700CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } + + /** + * @throws StoreException + */ + public function testRevertThrowsMigrationException(): void + { + $this->schemaManagerStub->method('dropTable') + ->willThrowException(new Exception('test')); + $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub); + $this->connectionStub->method('dbal')->willReturn($this->dbalStub); + + $migration = new Version20220801000700CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->revert(); + } + + /** + * @throws StoreException + */ + public function testRunThrowsOnIvalidTableName(): void + { + $this->connectionStub->method('preparePrefixedTableName') + ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table + + $migration = new Version20220801000700CreateConnectedServiceTable($this->connectionStub); + $this->expectException(MigrationException::class); + $migration->run(); + } +} diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php old mode 100644 new mode 100755 index aac918695cb0dd8c438fe6de50b1dba813b47445..acac97cbbf8a71c1e676dc551e7de5d1278ed52d --- a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php @@ -10,20 +10,20 @@ use Exception; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants - as BaseTableConstants; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as BaseTableConstants; use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store; use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository; use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants; use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection; use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; -use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2; use SimpleSAML\Module\accounting\Exceptions\StoreException; use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; use SimpleSAML\Test\Module\accounting\Constants\DateTime; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; /** * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository @@ -78,8 +78,6 @@ class RepositoryTest extends TestCase protected $connectionStub; protected string $spEntityIdHash; protected string $spMetadata; - protected string $clientIpAddress; - protected string $authenticationProtocolDesignation; /** * @throws StoreException @@ -133,8 +131,6 @@ class RepositoryTest extends TestCase $this->userAttributesHash = 'user-attributes-hash'; $this->createdAt = new DateTimeImmutable(); - $this->clientIpAddress = '123.123.123.123'; - $this->authenticationProtocolDesignation = Saml2::DESIGNATION; } public function testCanCreateInstance(): void @@ -194,17 +190,17 @@ class RepositoryTest extends TestCase $this->assertEquals( '1', $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] ); $this->assertSame( $this->spMetadata, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] ); $this->assertSame( $this->userAttributes, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); $connectedServiceId = (int)$this->repository->getConnectedService($idpSpUserVersionId)->fetchOne(); @@ -219,17 +215,17 @@ class RepositoryTest extends TestCase $this->assertEquals( '2', $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] ); $this->assertSame( $this->spMetadata, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] ); $this->assertSame( $this->userAttributes, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); // Simulate another SP @@ -258,17 +254,17 @@ class RepositoryTest extends TestCase $this->assertEquals( '1', $resultArray[$spEntityIdNew] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] ); $this->assertSame( $spMetadataNew, $resultArray[$spEntityIdNew] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] ); $this->assertSame( $this->userAttributes, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); // Simulate change in user attributes @@ -291,29 +287,29 @@ class RepositoryTest extends TestCase $this->assertEquals( '2', $resultArray[$spEntityIdNew] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] ); $this->assertSame( $spMetadataNew, $resultArray[$spEntityIdNew] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] ); // New SP with new user attributes version.. $this->assertSame( $userAttributesNew, $resultArray[$spEntityIdNew] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); // First SP still has old user attributes version... $this->assertSame( $this->userAttributes, $resultArray[$this->spEntityId] - [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] + [EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] ); } - public function testGetConnectedServiceProvidersThrowsOnInvalidDbal(): void + public function testGetConnectedServicesThrowsOnInvalidDbal(): void { $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); $repository = new Repository($this->connectionStub, $this->loggerStub); @@ -322,6 +318,33 @@ class RepositoryTest extends TestCase $repository->getConnectedServices($this->userIdentifierHash); } + public function testGetConnectedServiceThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->getConnectedService(1); + } + + public function testInsertConnectedServiceThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->insertConnectedService(1); + } + + public function testUpdatetConnectedServiceCountThrowsOnInvalidDbal(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->updateConnectedServiceVersionCount(1, new DateTimeImmutable()); + } + /** * @throws StoreException * @throws \Doctrine\DBAL\Exception @@ -380,4 +403,72 @@ class RepositoryTest extends TestCase $repository->deleteConnectedServicesOlderThan(new DateTimeImmutable()); } + + public function testCanTouchConnectedServiceVersionsTimestamp(): void + { + $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt); + $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative(); + $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID]; + $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt); + + $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt); + $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative(); + $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID]; + $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt); + + $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt); + $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative(); + $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID]; + $this->repository + ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt); + + $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative(); + $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative(); + $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative(); + + $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID]; + $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID]; + $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID]; + + $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt); + $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId) + ->fetchAssociative(); + + $idpSpUserVersionId = + (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID]; + + $authenticationAt = new DateTimeImmutable(); + + $this->repository->insertConnectedService($idpSpUserVersionId, $authenticationAt, $authenticationAt); + + $resultArray = $this->repository->getConnectedService($idpSpUserVersionId)->fetchAssociative(); + $this->assertSame( + $authenticationAt->format(DateTime::DEFAULT_FORMAT), + $resultArray[TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT] + ); + + $newAuthenticationAt = $authenticationAt->add(new DateInterval('P1D')); + + $this->repository->touchConnectedServiceVersionsTimestamp($userId, $spId, $newAuthenticationAt); + + $resultArray = $this->repository->getConnectedService($idpSpUserVersionId)->fetchAssociative(); + + $this->assertNotSame( + $authenticationAt->format(DateTime::DEFAULT_FORMAT), + $resultArray[TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT] + ); + $this->assertSame( + $newAuthenticationAt->format(DateTime::DEFAULT_FORMAT), + $resultArray[TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT] + ); + } + + public function testTouchConnectedServiceVersionsTimestampThrowsOnInvalidDbalForSelect(): void + { + $this->connectionStub->method('dbal')->willThrowException(new Exception('test')); + $repository = new Repository($this->connectionStub, $this->loggerStub); + $this->expectException(StoreException::class); + + $repository->touchConnectedServiceVersionsTimestamp(1, 1); + } } diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php old mode 100644 new mode 100755 index 3e810e1698ad9eee4d79d8af17f0d6fdefb1bbac..4dd1c1b7e54ad1d8704193d07be3fa574cb127ec --- a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php +++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php @@ -20,11 +20,15 @@ use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator; use SimpleSAML\Module\accounting\Entities\Authentication\Event; use SimpleSAML\Module\accounting\Entities\Authentication\Event\State; use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException; use SimpleSAML\Module\accounting\ModuleConfiguration; use SimpleSAML\Module\accounting\Services\HelpersManager; use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; use SimpleSAML\Test\Module\accounting\Constants\RawRowResult; use SimpleSAML\Test\Module\accounting\Constants\StateArrays; +// phpcs:ignore +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants as BaseTableConstants; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\EntityTableConstants; /** * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store @@ -161,6 +165,115 @@ class StoreTest extends TestCase ); } + + /** + * @throws StoreException + * @throws \Doctrine\DBAL\Exception + * @throws MigrationException + */ + public function testCanPersistAuthenticationEvent(): void + { + $store = new Store( + $this->moduleConfigurationStub, + $this->loggerMock, + null, + ModuleConfiguration\ConnectionType::MASTER, + $this->factoryStub + ); + $store->runSetup(); + + $idpCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $idpVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $spVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $idpSpUserVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + $connectedServiceCountQueryBuilder = $this->connection->dbal()->createQueryBuilder(); + + $idpCountQueryBuilder->select('COUNT(id) as idpCount')->from( + //'vds_idp' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP + ) + ); + $idpVersionCountQueryBuilder->select('COUNT(id) as idpVersionCount')->from( + //'vds_idp_version' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP_VERSION + ) + ); + $spCountQueryBuilder->select('COUNT(id) as spCount')->from( + //'vds_sp' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP + ) + ); + $spVersionCountQueryBuilder->select('COUNT(id) as spVersionCount')->from( + //'vds_sp_version' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP_VERSION + ) + ); + $userCountQueryBuilder->select('COUNT(id) as userCount')->from( + //'vds_user' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_USER + ) + ); + $userVersionCountQueryBuilder->select('COUNT(id) as userVersionCount')->from( + //'vds_user_version' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_USER_VERSION + ) + ); + $idpSpUserVersionCountQueryBuilder->select('COUNT(id) as idpSpUserVersionCount') + ->from( + //'vds_idp_sp_user_version' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP_SP_USER_VERSION + ) + ); + $connectedServiceCountQueryBuilder->select('COUNT(id) as connectedServiceCount') + ->from( + //'vds_connected_service' + $this->connection->preparePrefixedTableName( + BaseTableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_CONNECTED_SERVICE + ) + ); + + $this->assertSame(0, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(0, (int)$connectedServiceCountQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$connectedServiceCountQueryBuilder->executeQuery()->fetchOne()); + + $store->persist($this->authenticationEvent); + + $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne()); + $this->assertSame(1, (int)$connectedServiceCountQueryBuilder->executeQuery()->fetchOne()); + } + /** * @throws StoreException */ @@ -212,7 +325,7 @@ class StoreTest extends TestCase public function testGetConnectedOrganizationsThrowsForInvalidResult(): void { $rawResult = RawRowResult::CONNECTED_SERVICE; - unset($rawResult[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]); + unset($rawResult[EntityTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]); $this->repositoryMock->method('getConnectedServices') ->willReturn([$rawResult]); diff --git a/tests/src/Data/Stores/Bases/AbstractStoreTest.php b/tests/src/Data/Stores/Bases/AbstractStoreTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php b/tests/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Builders/DataStoreBuilderTest.php b/tests/src/Data/Stores/Builders/DataStoreBuilderTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Builders/JobsStoreBuilderTest.php b/tests/src/Data/Stores/Builders/JobsStoreBuilderTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Connections/Bases/AbstractMigratorTest.php b/tests/src/Data/Stores/Connections/Bases/AbstractMigratorTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Connections/DoctrineDbal/ConnectionTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/ConnectionTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Connections/DoctrineDbal/FactoryTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/FactoryTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Connections/DoctrineDbal/MigratorTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/MigratorTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/DoctrineDbal/StoreTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/StoreTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Stores/Jobs/PhpRedis/RedisStoreTest.php b/tests/src/Data/Stores/Jobs/PhpRedis/RedisStoreTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTrackerTest.php b/tests/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTrackerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2a9ef384472e7f624b1cf9167c76dddc42d273bb --- /dev/null +++ b/tests/src/Data/Trackers/Activity/DoctrineDbal/CurrentDataTrackerTest.php @@ -0,0 +1,190 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Trackers\Activity\DoctrineDbal; + +use DateInterval; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\CurrentDataTracker; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Module\accounting\Entities\Activity; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\CurrentDataTracker + * @uses \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\CurrentDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + */ +class CurrentDataTrackerTest extends TestCase +{ + /** + * @var Stub + */ + protected $moduleConfigurationStub; + /** + * @var MockObject + */ + protected $loggerMock; + /** + * @var MockObject + */ + protected $store; + + protected function setUp(): void + { + $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class); + $this->moduleConfigurationStub->method('getConnectionParameters') + ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->store = $this->createMock(Store::class); + } + + /** + * @throws StoreException + */ + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + CurrentDataTracker::class, + new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ) + ); + + $this->assertInstanceOf( + CurrentDataTracker::class, + new CurrentDataTracker($this->moduleConfigurationStub, $this->loggerMock) + ); + + $this->assertInstanceOf( + CurrentDataTracker::class, + CurrentDataTracker::build($this->moduleConfigurationStub, $this->loggerMock) + ); + } + + /** + * @throws StoreException + */ + public function testProcessCallsPersistOnDataStore(): void + { + $authenticationEventStub = $this->createStub(Event::class); + + $this->store->expects($this->once()) + ->method('persist') + ->with($authenticationEventStub); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->process($authenticationEventStub); + } + + /** + * @throws StoreException + */ + public function testSetupDependsOnDataStore(): void + { + $this->store->expects($this->exactly(2)) + ->method('needsSetup') + ->willReturn(true); + + $this->store->expects($this->once()) + ->method('runSetup'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $this->assertTrue($tracker->needsSetup()); + + $tracker->runSetup(); + } + + /** + * @throws StoreException + */ + public function testRunningSetupIfNotNeededLogsWarning(): void + { + $this->store->method('needsSetup') + ->willReturn(false); + + $this->loggerMock->expects($this->once()) + ->method('warning'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->runSetup(); + } + + /** + * @throws StoreException + */ + public function testGetActivity(): void + { + $activityBag = $this->createStub(Activity\Bag::class); + $this->store->expects($this->once()) + ->method('getActivity') + ->willReturn($activityBag); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $this->assertInstanceOf( + Activity\Bag::class, + $tracker->getActivity('test', 10, 0) + ); + } + + /** + * @throws StoreException + */ + public function testCanEnforceDataRetentionPolicy(): void + { + $retentionPolicy = new DateInterval('P10D'); + + $this->store->expects($this->once()) + ->method('deleteDataOlderThan'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->enforceDataRetentionPolicy($retentionPolicy); + } +} diff --git a/tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php b/tests/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTrackerTest.php old mode 100644 new mode 100755 similarity index 98% rename from tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php rename to tests/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTrackerTest.php index 4a9caee04e1f2b4be41883299e6275cb1b08674f..beea81a97e8617f40054c1b5bc63e10910936227 --- a/tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php +++ b/tests/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTrackerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimpleSAML\Test\Module\accounting\Data\Trackers\Activity\DoctrineDbal\Versioned; +namespace SimpleSAML\Test\Module\accounting\Data\Trackers\Activity\DoctrineDbal; use DateInterval; use PHPUnit\Framework\MockObject\MockObject; @@ -36,7 +36,7 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository */ -class DataTrackerTest extends TestCase +class VersionedDataTrackerTest extends TestCase { /** * @var Stub diff --git a/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php b/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php old mode 100644 new mode 100755 index ed5c86b7e479e56fe662e9bc38332178924b2b8d..05aa2ec802a94806bedcd3b36f93e3207237266f --- a/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php +++ b/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php @@ -21,7 +21,7 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration * @uses \SimpleSAML\Module\accounting\Services\HelpersManager */ -class AuthenticationDataTrackerBuilderTest extends TestCase +class DataTrackerBuilderTest extends TestCase { /** * @var MockObject diff --git a/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTrackerTest.php b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTrackerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3913e789ce613b922b202437e55bd777193c49d0 --- /dev/null +++ b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTrackerTest.php @@ -0,0 +1,192 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal; + +use DateInterval; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store; +use SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\CurrentDataTracker; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Entities\Authentication\Event; +use SimpleSAML\Module\accounting\Exceptions\StoreException; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; +use SimpleSAML\Module\accounting\Entities\ConnectedService; + +/** + * @covers \SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\CurrentDataTracker + * @uses \SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store + * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator + * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator + */ +class CurrentDataTrackerTest extends TestCase +{ + /** + * @var Stub + */ + protected $moduleConfigurationStub; + /** + * @var MockObject + */ + protected $loggerMock; + /** + * @var MockObject + */ + protected $store; + + protected function setUp(): void + { + $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class); + $this->moduleConfigurationStub->method('getConnectionParameters') + ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->store = $this->createMock( + Store::class + ); + } + + /** + * @throws StoreException + */ + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + CurrentDataTracker::class, + new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ) + ); + + $this->assertInstanceOf( + CurrentDataTracker::class, + new CurrentDataTracker($this->moduleConfigurationStub, $this->loggerMock) + ); + + $this->assertInstanceOf( + CurrentDataTracker::class, + CurrentDataTracker::build($this->moduleConfigurationStub, $this->loggerMock) + ); + } + + /** + * @throws StoreException + */ + public function testProcessCallsPersistOnDataStore(): void + { + $authenticationEventStub = $this->createStub(Event::class); + + $this->store->expects($this->once()) + ->method('persist') + ->with($authenticationEventStub); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->process($authenticationEventStub); + } + + /** + * @throws StoreException + */ + public function testSetupDependsOnDataStore(): void + { + $this->store->expects($this->exactly(2)) + ->method('needsSetup') + ->willReturn(true); + + $this->store->expects($this->once()) + ->method('runSetup'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $this->assertTrue($tracker->needsSetup()); + + $tracker->runSetup(); + } + + /** + * @throws StoreException + */ + public function testRunningSetupIfNotNeededLogsWarning(): void + { + $this->store->method('needsSetup') + ->willReturn(false); + + $this->loggerMock->expects($this->once()) + ->method('warning'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->runSetup(); + } + + /** + * @throws StoreException + */ + public function testGetConnectedServices(): void + { + $connectedOrganizationsBagStub = $this->createStub(ConnectedService\Bag::class); + $this->store->expects($this->once()) + ->method('getConnectedServices') + ->willReturn($connectedOrganizationsBagStub); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $this->assertInstanceOf( + ConnectedService\Bag::class, + $tracker->getConnectedServices('test') + ); + } + + /** + * @throws StoreException + */ + public function testCanEnforceDataRetentionPolicy(): void + { + $retentionPolicy = new DateInterval('P10D'); + + $this->store->expects($this->once()) + ->method('deleteDataOlderThan'); + + $tracker = new CurrentDataTracker( + $this->moduleConfigurationStub, + $this->loggerMock, + ModuleConfiguration\ConnectionType::MASTER, + $this->store + ); + + $tracker->enforceDataRetentionPolicy($retentionPolicy); + } +} diff --git a/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTrackerTest.php old mode 100644 new mode 100755 similarity index 98% rename from tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php rename to tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTrackerTest.php index 29fef34efba4c9110df8eed522144d690e0423f2..5ab6125500ea29064538eda2c3868c23586853ac --- a/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php +++ b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTrackerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimpleSAML\Test\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\Versioned; +namespace SimpleSAML\Test\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal; use DateInterval; use PHPUnit\Framework\MockObject\MockObject; @@ -36,7 +36,7 @@ use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters; * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository */ -class DataTrackerTest extends TestCase +class VersionedDataTrackerTest extends TestCase { /** * @var Stub diff --git a/tests/src/Entities/Activity/BagTest.php b/tests/src/Entities/Activity/BagTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/ActivityTest.php b/tests/src/Entities/ActivityTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Event/JobTest.php b/tests/src/Entities/Authentication/Event/JobTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Event/State/OidcTest.php b/tests/src/Entities/Authentication/Event/State/OidcTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Event/State/Saml2Test.php b/tests/src/Entities/Authentication/Event/State/Saml2Test.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/EventTest.php b/tests/src/Entities/Authentication/EventTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Protocol/BagTest.php b/tests/src/Entities/Authentication/Protocol/BagTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Protocol/OidcTest.php b/tests/src/Entities/Authentication/Protocol/OidcTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Authentication/Protocol/Saml2Test.php b/tests/src/Entities/Authentication/Protocol/Saml2Test.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Bases/AbstractJobTest.php b/tests/src/Entities/Bases/AbstractJobTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Bases/AbstractProviderTest.php b/tests/src/Entities/Bases/AbstractProviderTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Bases/AbstractStateTest.php b/tests/src/Entities/Bases/AbstractStateTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/ConnectedService/BagTest.php b/tests/src/Entities/ConnectedService/BagTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/ConnectedServiceTest.php b/tests/src/Entities/ConnectedServiceTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/GenericJobTest.php b/tests/src/Entities/GenericJobTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Entities/Providers/Identity/OidcTest.php b/tests/src/Entities/Providers/Identity/OidcTest.php old mode 100644 new mode 100755 index 450a0cd0af491775138cf2d9f0c305244ebcf136..91ab3e260936ecdbb74d3c17d9da8a6130916141 --- a/tests/src/Entities/Providers/Identity/OidcTest.php +++ b/tests/src/Entities/Providers/Identity/OidcTest.php @@ -44,4 +44,12 @@ class OidcTest extends TestCase new Oidc($metadata); } + + public function testCanGetProtocol(): void + { + $this->assertInstanceOf( + \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Oidc::class, + (new Oidc($this->metadata))->getProtocol() + ); + } } diff --git a/tests/src/Entities/Providers/Identity/Saml2Test.php b/tests/src/Entities/Providers/Identity/Saml2Test.php old mode 100644 new mode 100755 index f2214d846018171f658301194aec02319013fe2e..7c995952a1fee1d1a238a78853682c393d655c6f --- a/tests/src/Entities/Providers/Identity/Saml2Test.php +++ b/tests/src/Entities/Providers/Identity/Saml2Test.php @@ -46,4 +46,12 @@ class Saml2Test extends TestCase new Saml2($metadata); } + + public function testCanGetProtocol(): void + { + $this->assertInstanceOf( + \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2::class, + (new Saml2($this->metadata))->getProtocol() + ); + } } diff --git a/tests/src/Entities/Providers/Service/OidcTest.php b/tests/src/Entities/Providers/Service/OidcTest.php old mode 100644 new mode 100755 index 4827e5cff1c7f6cb285e603446e570cda3a42739..db62ad41c0a6337284d72342876b4e478248d3bc --- a/tests/src/Entities/Providers/Service/OidcTest.php +++ b/tests/src/Entities/Providers/Service/OidcTest.php @@ -46,4 +46,12 @@ class OidcTest extends TestCase new Oidc($metadata); } + + public function testCanGetProtocol(): void + { + $this->assertInstanceOf( + \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Oidc::class, + (new Oidc($this->metadata))->getProtocol() + ); + } } diff --git a/tests/src/Entities/Providers/Service/Saml2Test.php b/tests/src/Entities/Providers/Service/Saml2Test.php old mode 100644 new mode 100755 index f8565c18bc919e4edf58e540ca54c5adc47aa4c0..c4475d7089d65d70742a6b2b14d65b0860f72316 --- a/tests/src/Entities/Providers/Service/Saml2Test.php +++ b/tests/src/Entities/Providers/Service/Saml2Test.php @@ -41,4 +41,12 @@ class Saml2Test extends TestCase new Saml2($metadata); } + + public function testCanGetProtocol(): void + { + $this->assertInstanceOf( + \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2::class, + (new Saml2($this->metadata))->getProtocol() + ); + } } diff --git a/tests/src/Entities/UserTest.php b/tests/src/Entities/UserTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/ArrayTest.php b/tests/src/Helpers/ArrayTest.php old mode 100644 new mode 100755 index 0aeb6dfe523374800a057bc2bfefaa54b5e89903..dc35b097da155765d5f25dc94fa382e18091e05a --- a/tests/src/Helpers/ArrayTest.php +++ b/tests/src/Helpers/ArrayTest.php @@ -30,4 +30,44 @@ class ArrayTest extends TestCase $this->assertSame($unsorted, $sorted); } + + public function testCanGroupByValue(): void + { + $ungrouped = [ + ['a' => '1', 'b' => '1'], + ['a' => '1', 'b' => '2'], + ['a' => '2', 'b' => '3'], + ['a' => '2', 'b' => '4'], + ['a' => '3', 'b' => '5'], + ]; + + $expected = [ + '1' => [ + ['a' => '1', 'b' => '1'], + ['a' => '1', 'b' => '2'], + ], + '2' => [ + ['a' => '2', 'b' => '3'], + ['a' => '2', 'b' => '4'], + ], + '3' => [ + ['a' => '3', 'b' => '5'], + ], + ]; + + $this->assertNotSame($ungrouped, $expected); + $grouped = (new Arr())->groupByValue($ungrouped, 'a'); + $this->assertSame($grouped, $expected); + } + + public function testIsAssociative(): void + { + $nonAssociative = ['a', 'b', 'c']; + $associative = ['a' => 1, 'b' => 2, 'c' => 3]; + $mixed = ['a', 'b' => 2, 'c']; + + $this->assertFalse((new Arr())->isAssociative($nonAssociative)); + $this->assertTrue((new Arr())->isAssociative($associative)); + $this->assertTrue((new Arr())->isAssociative($mixed)); + } } diff --git a/tests/src/Helpers/AttributesTest.php b/tests/src/Helpers/AttributesTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/AuthenticationEventStateResolverTest.php b/tests/src/Helpers/AuthenticationEventStateResolverTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/DateTimeTest.php b/tests/src/Helpers/DateTimeTest.php old mode 100644 new mode 100755 index e4bbb57f3efa6fb143d47333a2978749b94a9137..d84163fc2afeeac875380b900d79efde59ab6a28 --- a/tests/src/Helpers/DateTimeTest.php +++ b/tests/src/Helpers/DateTimeTest.php @@ -26,4 +26,14 @@ class DateTimeTest extends TestCase $this->assertSame(1, (new DateTime())->convertDateIntervalToSeconds($interval)); } + + public function testToFormattedString(): void + { + $dateTime = new \DateTimeImmutable(); + + $this->assertSame( + $dateTime->format(DateTime::FORMAT_MYSQL), + (new DateTime())->toFormattedString($dateTime) + ); + } } diff --git a/tests/src/Helpers/EnvironmentTest.php b/tests/src/Helpers/EnvironmentTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/FilesystemTest.php b/tests/src/Helpers/FilesystemTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/HashTest.php b/tests/src/Helpers/HashTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationTest.php b/tests/src/Helpers/InstanceBuilderUsingModuleConfigurationTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/NetworkTest.php b/tests/src/Helpers/NetworkTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/ProviderResolverTest.php b/tests/src/Helpers/ProviderResolverTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Helpers/RandomTest.php b/tests/src/Helpers/RandomTest.php old mode 100644 new mode 100755 index 29fa51e895c02c3a17435abc33c2ef3060d5951c..5afe1cf4eb1eb652a166a739acf56dd8ade3f5f3 --- a/tests/src/Helpers/RandomTest.php +++ b/tests/src/Helpers/RandomTest.php @@ -16,4 +16,11 @@ class RandomTest extends TestCase { $this->assertIsInt((new Random())->getInt()); } + + public function testCanGetRandomString(): void + { + $this->assertIsString((new Random())->getString()); + + $this->assertSame(5, mb_strlen((new Random())->getString(5))); + } } diff --git a/tests/src/Helpers/RoutesTest.php b/tests/src/Helpers/RoutesTest.php old mode 100644 new mode 100755 index b649a1458867d538d1e2af76888772ed6031569e..3912741a65c6c6897e0f1de4777bf95a85c837dd --- a/tests/src/Helpers/RoutesTest.php +++ b/tests/src/Helpers/RoutesTest.php @@ -14,6 +14,7 @@ use SimpleSAML\Utils\HTTP; /** * @covers \SimpleSAML\Module\accounting\Helpers\Routes + * @uses \SimpleSAML\Module\accounting\Helpers\Arr */ class RoutesTest extends TestCase { @@ -53,4 +54,22 @@ class RoutesTest extends TestCase $this->assertSame($fullUrl, $moduleRoutesHelper->getUrl($path, $params)); } + + public function testCanAppendFragmentParameters(): void + { + $associativeFragments = ['a' => 'b']; + $indexedFragments = ['a', 'b']; + + $expectedUrlAssociative = self::BASE_URL . 'module.php/' . ModuleConfiguration::MODULE_NAME . '/path#a=b'; + $expectedUrlIndexed = self::BASE_URL . 'module.php/' . ModuleConfiguration::MODULE_NAME . '/path#a&b'; + + $this->assertSame( + $expectedUrlAssociative, + (new Routes($this->sspHttpUtilsStub))->getUrl('path', [], $associativeFragments) + ); + $this->assertSame( + $expectedUrlIndexed, + (new Routes($this->sspHttpUtilsStub))->getUrl('path', [], $indexedFragments) + ); + } } diff --git a/tests/src/Helpers/SspModuleTest.php b/tests/src/Helpers/SspModuleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7d1e90c31c6fe52ea9e2891f153d7baf9df8d6c7 --- /dev/null +++ b/tests/src/Helpers/SspModuleTest.php @@ -0,0 +1,19 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Helpers; + +use SimpleSAML\Module\accounting\Helpers\SspModule; +use PHPUnit\Framework\TestCase; + +/** + * @covers \SimpleSAML\Module\accounting\Helpers\SspModule + */ +class SspModuleTest extends TestCase +{ + public function testIsEnabled(): void + { + // Config file is at tests/config-templates/config.php + $this->assertFalse((new SspModule())->isEnabled('invalid')); + $this->assertTrue((new SspModule())->isEnabled('admin')); + } +} diff --git a/tests/src/ModuleConfigurationTest.php b/tests/src/ModuleConfigurationTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Services/AlertsBag/AlertTest.php b/tests/src/Services/AlertsBag/AlertTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5fc6af81022fa65043d29a527ec916c21a936433 --- /dev/null +++ b/tests/src/Services/AlertsBag/AlertTest.php @@ -0,0 +1,19 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services\AlertsBag; + +use SimpleSAML\Module\accounting\Services\AlertsBag\Alert; +use PHPUnit\Framework\TestCase; + +/** + * @covers \SimpleSAML\Module\accounting\Services\AlertsBag\Alert + */ +class AlertTest extends TestCase +{ + public function testCanInstantiateAlert(): void + { + $alert = new Alert('message', 'level'); + $this->assertSame('message', $alert->getMessage()); + $this->assertSame('level', $alert->getLevel()); + } +} diff --git a/tests/src/Services/AlertsBagTest.php b/tests/src/Services/AlertsBagTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9a165b73aa88c23bce88ddb7fb23e8871bce8752 --- /dev/null +++ b/tests/src/Services/AlertsBagTest.php @@ -0,0 +1,73 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services; + +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Services\AlertsBag; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Session; + +/** + * @covers \SimpleSAML\Module\accounting\Services\AlertsBag + */ +class AlertsBagTest extends TestCase +{ + protected Stub $alertStub; + protected MockObject $sessionMock; + + protected function setUp(): void + { + $this->alertStub = $this->createStub(AlertsBag\Alert::class); + $this->sessionMock = $this->createMock(Session::class); + } + + public function testCanConstruct(): void + { + $this->assertInstanceOf(AlertsBag::class, new AlertsBag($this->sessionMock)); + } + + public function testIsEmpty(): void + { + $this->sessionMock->method('getData')->willReturn([]); + $this->sessionMock->expects($this->never())->method('setData'); + + $this->assertFalse((new AlertsBag($this->sessionMock))->isNotEmpty()); + } + + public function testIsNotEmpty(): void + { + $this->sessionMock->method('getData')->willReturn([$this->alertStub]); + $this->sessionMock->expects($this->never())->method('setData'); + + $this->assertTrue((new AlertsBag($this->sessionMock))->isNotEmpty()); + } + + /** + * @throws Exception + */ + public function testGetAll(): void + { + $alerts = [$this->alertStub]; + $this->sessionMock->method('getData')->willReturn($alerts); + $this->sessionMock->expects($this->once())->method('setData'); + + $this->assertSame($alerts, (new AlertsBag($this->sessionMock))->getAll()); + } + + public function testGetAllThrowsForInvalidData(): void + { + $this->sessionMock->method('getData')->willReturn('invalid'); + $this->sessionMock->expects($this->never())->method('setData'); + + $this->expectException(Exception::class); + (new AlertsBag($this->sessionMock))->getAll(); + } + + public function testPutCallsSetDataOnSession(): void + { + $this->sessionMock->expects($this->once())->method('setData'); + (new AlertsBag($this->sessionMock))->put($this->alertStub); + } +} diff --git a/tests/src/Services/CsrfTokenTest.php b/tests/src/Services/CsrfTokenTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d05b570bc89651ebfe28880e94d5be5c04df7788 --- /dev/null +++ b/tests/src/Services/CsrfTokenTest.php @@ -0,0 +1,50 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services; + +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Services\CsrfToken; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Session; + +/** + * @covers \SimpleSAML\Module\accounting\Services\CsrfToken + */ +class CsrfTokenTest extends TestCase +{ + protected MockObject $sessionMock; + protected Stub $helpersManagerStub; + + protected function setUp(): void + { + $this->sessionMock = $this->createMock(Session::class); + $this->helpersManagerStub = $this->createStub(HelpersManager::class); + } + + protected function prepareMockedInstance(): CsrfToken + { + return new CsrfToken($this->sessionMock, $this->helpersManagerStub); + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf(CsrfToken::class, $this->prepareMockedInstance()); + } + + public function testCanGet(): void + { + $this->sessionMock->method('getData')->willReturn('sample'); + + $this->assertSame($this->prepareMockedInstance()->get(), 'sample'); + } + + public function testCanValidate(): void + { + $this->sessionMock->method('getData')->willReturn('sample'); + + $this->assertTrue($this->prepareMockedInstance()->validate('sample')); + $this->assertFalse($this->prepareMockedInstance()->validate('invalid')); + } +} diff --git a/tests/src/Services/HelpersManagerTest.php b/tests/src/Services/HelpersManagerTest.php old mode 100644 new mode 100755 index aad618084fdde23b22033c93d8930b73a1f8a8e8..5d9803de93ea5b905bae248795e4d78969171512 --- a/tests/src/Services/HelpersManagerTest.php +++ b/tests/src/Services/HelpersManagerTest.php @@ -16,6 +16,7 @@ use SimpleSAML\Module\accounting\Helpers\Routes; use SimpleSAML\Module\accounting\Helpers\Network; use SimpleSAML\Module\accounting\Helpers\ProviderResolver; use SimpleSAML\Module\accounting\Helpers\Random; +use SimpleSAML\Module\accounting\Helpers\SspModule; use SimpleSAML\Module\accounting\Services\HelpersManager; use PHPUnit\Framework\TestCase; @@ -48,5 +49,6 @@ class HelpersManagerTest extends TestCase $helpersManager->getAuthenticationEventStateResolver() ); $this->assertInstanceOf(ProviderResolver::class, $helpersManager->getProviderResolver()); + $this->assertInstanceOf(SspModule::class, $helpersManager->getSspModule()); } } diff --git a/tests/src/Services/JobRunner/RateLimiterTest.php b/tests/src/Services/JobRunner/RateLimiterTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Services/JobRunner/StateTest.php b/tests/src/Services/JobRunner/StateTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Services/JobRunnerTest.php b/tests/src/Services/JobRunnerTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Services/LoggerServiceTest.php b/tests/src/Services/LoggerServiceTest.php old mode 100644 new mode 100755 diff --git a/tests/src/Services/MenuManager/MenuItemTest.php b/tests/src/Services/MenuManager/MenuItemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9c0bdc97b28209174dc91f4f9a3ef8123ecb1499 --- /dev/null +++ b/tests/src/Services/MenuManager/MenuItemTest.php @@ -0,0 +1,20 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services\MenuManager; + +use SimpleSAML\Module\accounting\Services\MenuManager\MenuItem; +use PHPUnit\Framework\TestCase; + +/** + * @covers \SimpleSAML\Module\accounting\Services\MenuManager\MenuItem + */ +class MenuItemTest extends TestCase +{ + public function testCanInstantiateMenuItem(): void + { + $menuItem = new MenuItem('hrefPath', 'label', 'iconAssetPath'); + $this->assertSame('hrefPath', $menuItem->getHrefPath()); + $this->assertSame('label', $menuItem->getLabel()); + $this->assertSame('iconAssetPath', $menuItem->getIconAssetPath()); + } +} diff --git a/tests/src/Services/MenuManagerTest.php b/tests/src/Services/MenuManagerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..219ba1c3a329637c2075a3a1b58271342393ed4f --- /dev/null +++ b/tests/src/Services/MenuManagerTest.php @@ -0,0 +1,32 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services; + +use PHPUnit\Framework\MockObject\Stub; +use SimpleSAML\Module\accounting\Services\MenuManager; +use PHPUnit\Framework\TestCase; + +/** + * @covers \SimpleSAML\Module\accounting\Services\MenuManager + */ +class MenuManagerTest extends TestCase +{ + protected Stub $menuItemStub; + + protected function setUp(): void + { + $this->menuItemStub = $this->createStub(MenuManager\MenuItem::class); + } + + public function testCanWorkWithItems(): void + { + $menuManager = new MenuManager(); + + $this->assertEmpty($menuManager->getItems()); + $menuManager->addItem($this->menuItemStub); + $this->assertNotEmpty($menuManager->getItems()); + $this->assertCount(1, $menuManager->getItems()); + $menuManager->addItems($this->menuItemStub, $this->menuItemStub); + $this->assertCount(3, $menuManager->getItems()); + } +} diff --git a/tests/src/Services/SspModuleManagerTest.php b/tests/src/Services/SspModuleManagerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..caa8be1e054742e2acb5fbac7c258b8fcedb4447 --- /dev/null +++ b/tests/src/Services/SspModuleManagerTest.php @@ -0,0 +1,36 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services; + +use PHPUnit\Framework\MockObject\Stub; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Module\accounting\Services\SspModuleManager; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\accounting\SspModule\Oidc; + +/** + * @covers \SimpleSAML\Module\accounting\Services\SspModuleManager + * @uses \SimpleSAML\Module\accounting\SspModule\Oidc + */ +class SspModuleManagerTest extends TestCase +{ + protected Stub $loggerStub; + protected Stub $helpersMangerStub; + + protected function setUp(): void + { + $this->loggerStub = $this->createStub(LoggerInterface::class); + $this->helpersMangerStub = $this->createStub(HelpersManager::class); + } + + public function testGet(): void + { + $sspModuleManager = new SspModuleManager($this->loggerStub, $this->helpersMangerStub); + + // By default, OIDC module will try to connect to the database, which we will not mock in module manager. + $this->expectException(\Throwable::class); + $sspModuleManager->getOidc(); + } +} diff --git a/tests/src/Services/TrackerResolverTest.php b/tests/src/Services/TrackerResolverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c20273f9dc3136e1e831315dffa662e7c46cf89c --- /dev/null +++ b/tests/src/Services/TrackerResolverTest.php @@ -0,0 +1,81 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\Services; + +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder; +use SimpleSAML\Module\accounting\Data\Providers\Interfaces\DataProviderInterface; +use SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder; +use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface; +use SimpleSAML\Module\accounting\Exceptions\Exception; +use SimpleSAML\Module\accounting\ModuleConfiguration; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Module\accounting\Services\TrackerResolver; +use PHPUnit\Framework\TestCase; + +/** + * @covers \SimpleSAML\Module\accounting\Services\TrackerResolver + */ +class TrackerResolverTest extends TestCase +{ + protected MockObject $moduleConfigurationMock; + protected MockObject $loggerMock; + protected MockObject $helpersManagerMock; + protected MockObject $dataProviderBuilderMock; + protected MockObject $dataTrackerBuilderMock; + protected MockObject $dataProviderMock; + protected MockObject $dataTrackerMock; + + protected function setUp(): void + { + $this->moduleConfigurationMock = $this->createMock(ModuleConfiguration::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->helpersManagerMock = $this->createMock(HelpersManager::class); + $this->dataProviderBuilderMock = $this->createMock(DataProviderBuilder::class); + $this->dataTrackerBuilderMock = $this->createMock(DataTrackerBuilder::class); + + $this->dataProviderMock = $this->createMock(DataProviderInterface::class); + $this->dataTrackerMock = $this->createMock(DataTrackerInterface::class); + } + + public function testCanConstruct(): void + { + $this->assertInstanceOf( + TrackerResolver::class, + new TrackerResolver( + $this->moduleConfigurationMock, + $this->loggerMock, + $this->helpersManagerMock, + $this->dataProviderBuilderMock, + $this->dataTrackerBuilderMock + ) + ); + } + + /** + * @throws Exception + */ + public function testFromModuleConfiguration(): void + { + $this->moduleConfigurationMock->method('getProviderClasses')->willReturn( + [DataProviderInterface::class] + ); + $this->dataProviderMock->method('getTracker')->willReturn($this->dataTrackerMock); + $this->dataProviderBuilderMock->method('build')->willReturn($this->dataProviderMock); + $this->moduleConfigurationMock->method('getAdditionalTrackers')->willReturn( + [DataTrackerInterface::class] + ); + $this->dataTrackerBuilderMock->method('build')->willReturn($this->dataTrackerMock); + + $trackerResolver = new TrackerResolver( + $this->moduleConfigurationMock, + $this->loggerMock, + $this->helpersManagerMock, + $this->dataProviderBuilderMock, + $this->dataTrackerBuilderMock + ); + + $this->assertCount(2, $trackerResolver->fromModuleConfiguration()); + } +} diff --git a/tests/src/SspModule/OidcTest.php b/tests/src/SspModule/OidcTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fd3b72ccadb4bd348a30b1801de5fa42e7adc3c9 --- /dev/null +++ b/tests/src/SspModule/OidcTest.php @@ -0,0 +1,146 @@ +<?php + +namespace SimpleSAML\Test\Module\accounting\SspModule; + +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use SimpleSAML\Database; +use SimpleSAML\Module\accounting\Helpers\DateTime; +use SimpleSAML\Module\accounting\Helpers\SspModule; +use SimpleSAML\Module\accounting\Services\HelpersManager; +use SimpleSAML\Module\accounting\SspModule\Oidc; +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\oidc\Services\Container; + +/** + * @covers \SimpleSAML\Module\accounting\SspModule\Oidc + */ +class OidcTest extends TestCase +{ + protected MockObject $loggerMock; + protected MockObject $helpersManagerMock; + protected MockObject $containerMock; + protected MockObject $databaseMock; + protected MockObject $sspModuleMock; + protected MockObject $dateTimeMock; + protected MockObject $pdoStatementMock; + + protected function setUp(): void + { + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->helpersManagerMock = $this->createMock(HelpersManager::class); + $this->containerMock = $this->createMock(Container::class); + $this->databaseMock = $this->createMock(Database::class); + + $this->sspModuleMock = $this->createMock(SspModule::class); + $this->dateTimeMock = $this->createMock(DateTime::class); + $this->pdoStatementMock = $this->createMock(\PDOStatement::class); + } + + protected function prepareMockedInstance(): Oidc + { + $this->helpersManagerMock->method('getSspModule')->willReturn($this->sspModuleMock); + $this->helpersManagerMock->method('getDateTime')->willReturn($this->dateTimeMock); + + return new Oidc( + $this->loggerMock, + $this->helpersManagerMock, + $this->containerMock, + $this->databaseMock + ); + } + + public function testCanCreateInstance(): void + { + $this->assertInstanceOf( + Oidc::class, + $this->prepareMockedInstance() + ); + } + + public function testIsEnabled(): void + { + $this->sspModuleMock->method('isEnabled')->willReturn(true); + $this->assertTrue($this->prepareMockedInstance()->isEnabled()); + } + + public function testIsNotEnabled(): void + { + $this->sspModuleMock->method('isEnabled')->willReturn(false); + $this->assertFalse($this->prepareMockedInstance()->isEnabled()); + } + + public function testCanGetContainer(): void + { + $this->assertInstanceOf( + Container::class, + $this->prepareMockedInstance()->getContainer() + ); + } + + public function testGetUsersAccessTokens(): void + { + $this->pdoStatementMock->method('fetchAll')->willReturn(['sample']); + $this->databaseMock->method('read')->willReturn($this->pdoStatementMock); + + $this->assertNotEmpty( + $this->prepareMockedInstance()->getUsersAccessTokens('userId', ['clientId']) + ); + } + + public function testGetUsersAccessTokensEmpty(): void + { + $this->databaseMock->expects($this->once())->method('read'); + $this->assertEmpty( + $this->prepareMockedInstance()->getUsersAccessTokens('userId', ['clientId']) + ); + } + + public function testRevokeUsersAccessToken(): void + { + $this->databaseMock->expects($this->once())->method('write'); + $this->prepareMockedInstance()->revokeUsersAccessToken('userId', 'accessTokenId'); + } + + public function testGetUsersRefreshTokens(): void + { + $this->pdoStatementMock->method('fetchAll')->willReturn(['sample']); + $this->databaseMock->method('read')->willReturn($this->pdoStatementMock); + + $this->assertNotEmpty( + $this->prepareMockedInstance()->getUsersRefreshTokens('userId', ['clientId']) + ); + } + + public function testGetUsersRefreshTokensEmpty(): void + { + $this->databaseMock->expects($this->once())->method('read'); + $this->assertEmpty( + $this->prepareMockedInstance()->getUsersRefreshTokens('userId', ['clientId']) + ); + } + + public function testRevokeUsersRefreshToken(): void + { + $this->databaseMock->expects($this->once())->method('write'); + $this->prepareMockedInstance()->revokeUsersRefreshToken('userId', 'refreshTokenId'); + } + + public function testGetClients(): void + { + $this->pdoStatementMock->method('fetchAll')->willReturn(['sample']); + $this->databaseMock->method('read')->willReturn($this->pdoStatementMock); + + $this->assertNotEmpty( + $this->prepareMockedInstance()->getClients(['clientId']) + ); + } + + public function testGetClientsEmpty(): void + { + $this->databaseMock->expects($this->once())->method('read'); + $this->assertEmpty( + $this->prepareMockedInstance()->getClients(['clientId']) + ); + } +}