From 8cd607b063484b731577c53ffde2244b6fc0cf9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danyi=20D=C3=A1vid?= Date: Sat, 12 May 2018 00:14:34 +0200 Subject: [PATCH] * some stuff moved to UtilityModule(old DoctrineExpressiveModule) * api auth in place --- bin/cli | 18 + composer.json | 6 +- composer.lock | 373 +++++++++++++++++- config/autoload/auth.global.php | 12 + config/autoload/dependencies.global.php | 1 - config/autoload/local.php.dist | 3 + config/config.php | 2 +- config/pipeline.php | 3 +- config/routes.php | 3 + src/App/Handler/Api/AwardeeHandler.php | 1 + src/App/Handler/Api/JudgesHandler.php | 1 + src/App/Handler/Api/YearsHandler.php | 1 + src/App/Service/AwardeeManager.php | 2 +- src/App/Service/AwardeeManagerFactory.php | 2 +- src/App/Service/JudgeManager.php | 2 +- src/App/Service/JudgeManagerFactory.php | 2 +- .../Command/GeneratePasswordHash.php | 34 ++ .../Command/GeneratePasswordHashFactory.php | 19 + .../ConfigProvider.php | 22 +- .../Form/Element/ElementFactory.php | 2 +- .../InvalidRepositoryResultException.php | 2 +- .../Form/Element/ObjectMultiCheckbox.php | 2 +- .../Form/Element/ObjectRadio.php | 4 +- .../Form/Element/ObjectSelect.php | 4 +- .../Form/Element/Proxy.php | 2 +- .../Handler}/AbstractCrudHandler.php | 2 +- src/UtilityModule/Handler/AuthHandler.php | 49 +++ .../Handler/AuthHandlerFactory.php | 18 + .../Hydrator/DoctrineObject.php | 2 +- .../Hydrator/DoctrineObjectFactory.php | 2 +- .../Hydrator/Filter/PropertyName.php | 2 +- .../Strategy/AbstractCollectionStrategy.php | 2 +- .../Strategy/AllowRemoveByReference.php | 2 +- .../Hydrator/Strategy/AllowRemoveByValue.php | 2 +- .../Strategy/DisallowRemoveByReference.php | 2 +- .../Strategy/DisallowRemoveByValue.php | 2 +- .../Middleware/CorsMiddlewareFactory.php | 2 +- .../Middleware/JwtMiddlewareFactory.php | 69 ++++ src/UtilityModule/Service/AuthService.php | 97 +++++ .../Service/AuthServiceFactory.php | 19 + .../Validator/NoObjectExists.php | 0 .../Validator/ObjectExists.php | 0 .../Service/AbstractValidatorFactory.php | 0 .../Exception/ServiceCreationException.php | 0 .../Service/NoObjectExistsFactory.php | 0 .../Validator/Service/ObjectExistsFactory.php | 0 .../Validator/Service/UniqueObjectFactory.php | 0 .../Validator/UniqueObject.php | 0 48 files changed, 766 insertions(+), 29 deletions(-) create mode 100755 bin/cli create mode 100644 config/autoload/auth.global.php create mode 100644 src/UtilityModule/Command/GeneratePasswordHash.php create mode 100644 src/UtilityModule/Command/GeneratePasswordHashFactory.php rename src/{DoctrineExpressiveModule => UtilityModule}/ConfigProvider.php (67%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/ElementFactory.php (89%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/Exception/InvalidRepositoryResultException.php (66%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/ObjectMultiCheckbox.php (97%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/ObjectRadio.php (93%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/ObjectSelect.php (95%) rename src/{DoctrineExpressiveModule => UtilityModule}/Form/Element/Proxy.php (99%) rename src/{App/Handler/Api => UtilityModule/Handler}/AbstractCrudHandler.php (99%) create mode 100644 src/UtilityModule/Handler/AuthHandler.php create mode 100644 src/UtilityModule/Handler/AuthHandlerFactory.php rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/DoctrineObject.php (99%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/DoctrineObjectFactory.php (85%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Filter/PropertyName.php (97%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Strategy/AbstractCollectionStrategy.php (98%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Strategy/AllowRemoveByReference.php (97%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Strategy/AllowRemoveByValue.php (98%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Strategy/DisallowRemoveByReference.php (97%) rename src/{DoctrineExpressiveModule => UtilityModule}/Hydrator/Strategy/DisallowRemoveByValue.php (98%) rename src/{DoctrineExpressiveModule => UtilityModule}/Middleware/CorsMiddlewareFactory.php (88%) create mode 100644 src/UtilityModule/Middleware/JwtMiddlewareFactory.php create mode 100644 src/UtilityModule/Service/AuthService.php create mode 100644 src/UtilityModule/Service/AuthServiceFactory.php rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/NoObjectExists.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/ObjectExists.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/Service/AbstractValidatorFactory.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/Service/Exception/ServiceCreationException.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/Service/NoObjectExistsFactory.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/Service/ObjectExistsFactory.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/Service/UniqueObjectFactory.php (100%) rename src/{DoctrineExpressiveModule => UtilityModule}/Validator/UniqueObject.php (100%) diff --git a/bin/cli b/bin/cli new file mode 100755 index 0000000..e6e5ecb --- /dev/null +++ b/bin/cli @@ -0,0 +1,18 @@ +#!/usr/bin/php +get('config')['console']['commands']; +foreach ($commands as $command) { + $application->add($container->get($command)); +} + +$application->run(); diff --git a/composer.json b/composer.json index 88dfd9e..29d8865 100644 --- a/composer.json +++ b/composer.json @@ -46,8 +46,11 @@ "los/loslog": "^3.1", "roave/security-advisories": "dev-master", "tuupola/cors-middleware": "^0.7.0", + "tuupola/slim-jwt-auth": "^3.0", "zendframework/zend-component-installer": "^2.1.1", + "zendframework/zend-config": "^3.2", "zendframework/zend-config-aggregator": "^1.0", + "zendframework/zend-crypt": "^3.3", "zendframework/zend-diactoros": "^1.7.1", "zendframework/zend-expressive": "^3.0.1", "zendframework/zend-expressive-fastroute": "^3.0", @@ -55,6 +58,7 @@ "zendframework/zend-expressive-platesrenderer": "^2.0", "zendframework/zend-hydrator": "^2.4", "zendframework/zend-json": "^3.1", + "zendframework/zend-permissions-rbac": "^3.0", "zendframework/zend-servicemanager": "^3.3", "zendframework/zend-stdlib": "^3.1" }, @@ -68,7 +72,7 @@ "autoload": { "psr-4": { "App\\": "src/App/", - "DoctrineExpressiveModule\\": "src/DoctrineExpressiveModule/" + "UtilityModule\\": "src/UtilityModule/" } }, "autoload-dev": { diff --git a/composer.lock b/composer.lock index 7048ea0..b8f1e9c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "d431f9aedef0713895d247778391bea1", + "content-hash": "c16dbd46a5e34757c69f0fdb32bf7680", "packages": [ { "name": "behat/transliterator", @@ -784,6 +784,52 @@ ], "time": "2017-02-09T16:10:21+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": " 4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "time": "2017-06-27T22:17:23+00:00" + }, { "name": "gedmo/doctrine-extensions", "version": "v2.4.34", @@ -1274,6 +1320,54 @@ ], "time": "2018-02-13T20:26:39+00:00" }, + { + "name": "paragonie/random_compat", + "version": "v2.0.12", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2018-04-04T21:24:14+00:00" + }, { "name": "psr/container", "version": "1.0.0", @@ -1976,6 +2070,73 @@ ], "time": "2017-07-15T22:03:15+00:00" }, + { + "name": "tuupola/slim-jwt-auth", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/tuupola/slim-jwt-auth.git", + "reference": "b195031f3732bc2e050bbb203329825377b9f8cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tuupola/slim-jwt-auth/zipball/b195031f3732bc2e050bbb203329825377b9f8cb", + "reference": "b195031f3732bc2e050bbb203329825377b9f8cb", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^3.0 || ^4.0 || ^5.0", + "php": "^7.1", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.0", + "tuupola/callable-handler": "^0.3.0", + "tuupola/http-factory": "^0.3.0" + }, + "require-dev": { + "codedungeon/phpunit-result-printer": "^0.5.4", + "equip/dispatch": "^2.0", + "overtrue/phplint": "^0.2.4", + "phpstan/phpstan": "^0.9.1", + "phpunit/phpunit": "^6.5", + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-diactoros": "^1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Tuupola\\Middleware\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "http://www.appelsiini.net/", + "role": "Developer" + } + ], + "description": "PSR-7 and PSR-15 JWT Authentication Middleware", + "homepage": "https://github.com/tuupola/slim-jwt-auth", + "keywords": [ + "auth", + "json", + "jwt", + "middleware", + "psr-15", + "psr-7" + ], + "time": "2018-03-02T06:33:46+00:00" + }, { "name": "zendframework/zend-component-installer", "version": "2.1.1", @@ -2028,6 +2189,66 @@ ], "time": "2018-03-21T16:53:56+00:00" }, + { + "name": "zendframework/zend-config", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-config.git", + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/6796f5dcba52c84ef2501d7313618989b5ef3023", + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0" + }, + "require-dev": { + "malukenho/docheader": "^0.1.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-filter": "^2.7.2", + "zendframework/zend-i18n": "^2.7.4", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3" + }, + "suggest": { + "zendframework/zend-filter": "^2.7.2; install if you want to use the Filter processor", + "zendframework/zend-i18n": "^2.7.4; install if you want to use the Translator processor", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3; if you need an extensible plugin manager for use with the Config Factory" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "keywords": [ + "ZendFramework", + "config", + "zf" + ], + "time": "2018-04-24T19:26:44+00:00" + }, { "name": "zendframework/zend-config-aggregator", "version": "1.1.1", @@ -2083,6 +2304,58 @@ ], "time": "2018-04-04T20:37:31+00:00" }, + { + "name": "zendframework/zend-crypt", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-crypt.git", + "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/9c2916faa9b2132a0f91cdca8e95b025c352f065", + "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "ext-mbstring": "*", + "php": "^5.6 || ^7.0", + "zendframework/zend-math": "^3.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "suggest": { + "ext-openssl": "Required for most features of Zend\\Crypt" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Crypt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Strong cryptography tools and password hashing", + "keywords": [ + "ZendFramework", + "crypt", + "zf" + ], + "time": "2018-04-24T22:01:58+00:00" + }, { "name": "zendframework/zend-diactoros", "version": "1.7.1", @@ -2817,6 +3090,104 @@ ], "time": "2018-04-09T21:59:51+00:00" }, + { + "name": "zendframework/zend-math", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-math.git", + "reference": "558806e338ee68575fbe69489c9dcb6d57a1dae0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-math/zipball/558806e338ee68575fbe69489c9dcb6d57a1dae0", + "reference": "558806e338ee68575fbe69489c9dcb6d57a1dae0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "paragonie/random_compat": "^2.0.11", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "suggest": { + "ext-bcmath": "If using the bcmath functionality", + "ext-gmp": "If using the gmp functionality" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev", + "dev-develop": "3.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Create cryptographically secure pseudo-random numbers, and manage big integers", + "keywords": [ + "ZendFramework", + "math", + "zf" + ], + "time": "2018-04-26T21:37:02+00:00" + }, + { + "name": "zendframework/zend-permissions-rbac", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-permissions-rbac.git", + "reference": "850eda612474d93f49b484604529bcd8ac810d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-permissions-rbac/zipball/850eda612474d93f49b484604529bcd8ac810d8c", + "reference": "850eda612474d93f49b484604529bcd8ac810d8c", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0.1", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev", + "dev-develop": "3.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Permissions\\Rbac\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Provides a role-based access control management", + "homepage": "https://github.com/zendframework/zend-permissions-rbac", + "keywords": [ + "ZendFramework", + "authorization", + "rbac", + "zend-permssions-rbac" + ], + "time": "2018-03-22T13:18:45+00:00" + }, { "name": "zendframework/zend-servicemanager", "version": "3.3.2", diff --git a/config/autoload/auth.global.php b/config/autoload/auth.global.php new file mode 100644 index 0000000..8eaa66b --- /dev/null +++ b/config/autoload/auth.global.php @@ -0,0 +1,12 @@ + [ + 'unguarded_routes' => [ + 'api.auth.login', + 'api.ping', + ], + ], +]; diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 479de17..a9ab249 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -21,7 +21,6 @@ return [ // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ // Fully\Qualified\ClassName::class => Fully\Qualified\FactoryName::class, - Tuupola\Middleware\CorsMiddleware::class => DoctrineExpressiveModule\Middleware\CorsMiddlewareFactory::class, ], ], ]; diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist index ec825d4..8e3fc8c 100644 --- a/config/autoload/local.php.dist +++ b/config/autoload/local.php.dist @@ -9,4 +9,7 @@ declare(strict_types=1); return [ + 'acl_config' => [ + 'hmac_key' => '', + ], ]; diff --git a/config/config.php b/config/config.php index a4f3f20..0ace694 100644 --- a/config/config.php +++ b/config/config.php @@ -26,7 +26,7 @@ $aggregator = new ConfigAggregator([ \Zend\Expressive\Router\ConfigProvider::class, // Default App module config - DoctrineExpressiveModule\ConfigProvider::class, + UtilityModule\ConfigProvider::class, App\ConfigProvider::class, // Load application config in a pre-defined order in such a way that local settings diff --git a/config/pipeline.php b/config/pipeline.php index b838955..6c5035a 100644 --- a/config/pipeline.php +++ b/config/pipeline.php @@ -11,7 +11,6 @@ use Zend\Expressive\Helper\UrlHelperMiddleware; use Zend\Expressive\MiddlewareFactory; use Zend\Expressive\Router\Middleware\DispatchMiddleware; use Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware; -use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware; use Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware; use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Stratigility\Middleware\ErrorHandler; @@ -54,7 +53,6 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac // Order here matters; the MethodNotAllowedMiddleware should be placed // after the Implicit*Middleware. $app->pipe(ImplicitHeadMiddleware::class); -// $app->pipe(ImplicitOptionsMiddleware::class); $app->pipe(CorsMiddleware::class); $app->pipe(MethodNotAllowedMiddleware::class); @@ -67,6 +65,7 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac // - route-based authentication // - route-based validation // - etc. + $app->pipe(Tuupola\Middleware\JwtAuthentication::class); // Register the dispatch middleware in the middleware pipeline $app->pipe(DispatchMiddleware::class); diff --git a/config/routes.php b/config/routes.php index 3d8cbce..c5a33f2 100644 --- a/config/routes.php +++ b/config/routes.php @@ -61,4 +61,7 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac $app->get('/awards', App\Handler\AwardeeRedirectHandler::class, 'awardees'); $app->get('/awards/{year:\d+}', App\Handler\AwardeeHandler::class, 'awardees-by-year'); $app->get('/awardee/{slug}', App\Handler\ProfileHandler::class, 'awardee'); + + $app->post('/api/auth/login', UtilityModule\Handler\AuthHandler::class, 'api.auth.login'); + $app->get('/api/auth/renew', UtilityModule\Handler\AuthHandler::class, 'api.auth.renew'); }; diff --git a/src/App/Handler/Api/AwardeeHandler.php b/src/App/Handler/Api/AwardeeHandler.php index 1bdfcee..4cb993e 100644 --- a/src/App/Handler/Api/AwardeeHandler.php +++ b/src/App/Handler/Api/AwardeeHandler.php @@ -7,6 +7,7 @@ namespace App\Handler\Api; use App\Service\AwardeeManager; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use UtilityModule\Handler\AbstractCrudHandler; use Zend\Diactoros\Response\JsonResponse; class AwardeeHandler extends AbstractCrudHandler diff --git a/src/App/Handler/Api/JudgesHandler.php b/src/App/Handler/Api/JudgesHandler.php index 42795fb..a98e845 100644 --- a/src/App/Handler/Api/JudgesHandler.php +++ b/src/App/Handler/Api/JudgesHandler.php @@ -7,6 +7,7 @@ namespace App\Handler\Api; use App\Service\JudgeManager; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use UtilityModule\Handler\AbstractCrudHandler; use Zend\Diactoros\Response\JsonResponse; class JudgesHandler extends AbstractCrudHandler diff --git a/src/App/Handler/Api/YearsHandler.php b/src/App/Handler/Api/YearsHandler.php index cafe9bb..128428b 100644 --- a/src/App/Handler/Api/YearsHandler.php +++ b/src/App/Handler/Api/YearsHandler.php @@ -7,6 +7,7 @@ namespace App\Handler\Api; use App\Service\YearManager; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use UtilityModule\Handler\AbstractCrudHandler; use Zend\Diactoros\Response\JsonResponse; class YearsHandler extends AbstractCrudHandler diff --git a/src/App/Service/AwardeeManager.php b/src/App/Service/AwardeeManager.php index 146b9d9..b944e1f 100644 --- a/src/App/Service/AwardeeManager.php +++ b/src/App/Service/AwardeeManager.php @@ -6,7 +6,7 @@ namespace App\Service; use App\Entity\Awardee; use Doctrine\ORM\EntityManager; -use DoctrineExpressiveModule\Hydrator\DoctrineObject; +use UtilityModule\Hydrator\DoctrineObject; class AwardeeManager { diff --git a/src/App/Service/AwardeeManagerFactory.php b/src/App/Service/AwardeeManagerFactory.php index 5683608..8b08e08 100644 --- a/src/App/Service/AwardeeManagerFactory.php +++ b/src/App/Service/AwardeeManagerFactory.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Service; use Doctrine\ORM\EntityManager; -use DoctrineExpressiveModule\Hydrator\DoctrineObject; +use UtilityModule\Hydrator\DoctrineObject; use Psr\Container\ContainerInterface; class AwardeeManagerFactory diff --git a/src/App/Service/JudgeManager.php b/src/App/Service/JudgeManager.php index 2df996d..19923c5 100644 --- a/src/App/Service/JudgeManager.php +++ b/src/App/Service/JudgeManager.php @@ -6,7 +6,7 @@ namespace App\Service; use App\Entity\Judge; use Doctrine\ORM\EntityManager; -use DoctrineExpressiveModule\Hydrator\DoctrineObject; +use UtilityModule\Hydrator\DoctrineObject; class JudgeManager { diff --git a/src/App/Service/JudgeManagerFactory.php b/src/App/Service/JudgeManagerFactory.php index 39694cb..b2c4aef 100644 --- a/src/App/Service/JudgeManagerFactory.php +++ b/src/App/Service/JudgeManagerFactory.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Service; use Doctrine\ORM\EntityManager; -use DoctrineExpressiveModule\Hydrator\DoctrineObject; +use UtilityModule\Hydrator\DoctrineObject; use Psr\Container\ContainerInterface; class JudgeManagerFactory diff --git a/src/UtilityModule/Command/GeneratePasswordHash.php b/src/UtilityModule/Command/GeneratePasswordHash.php new file mode 100644 index 0000000..84794a9 --- /dev/null +++ b/src/UtilityModule/Command/GeneratePasswordHash.php @@ -0,0 +1,34 @@ +authService = $authService; + parent::__construct(); + } + + protected function configure() + { + $this->setName('password:generate') + ->setDescription('Generate a password hash') + ->addArgument('password', InputArgument::REQUIRED, 'Password to hash'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $pass = $input->getArgument('password'); + $output->writeln($this->authService->createPasswordHash($pass)); + } +} diff --git a/src/UtilityModule/Command/GeneratePasswordHashFactory.php b/src/UtilityModule/Command/GeneratePasswordHashFactory.php new file mode 100644 index 0000000..f4f7565 --- /dev/null +++ b/src/UtilityModule/Command/GeneratePasswordHashFactory.php @@ -0,0 +1,19 @@ +get(AuthService::class); + return new GeneratePasswordHash($authService); + } +} diff --git a/src/DoctrineExpressiveModule/ConfigProvider.php b/src/UtilityModule/ConfigProvider.php similarity index 67% rename from src/DoctrineExpressiveModule/ConfigProvider.php rename to src/UtilityModule/ConfigProvider.php index 9a993db..8191a5e 100644 --- a/src/DoctrineExpressiveModule/ConfigProvider.php +++ b/src/UtilityModule/ConfigProvider.php @@ -2,8 +2,10 @@ declare(strict_types=1); -namespace DoctrineExpressiveModule; +namespace UtilityModule; +use Tuupola\Middleware\CorsMiddleware; +use Tuupola\Middleware\JwtAuthentication; use Zend\EventManager\EventManager; /** @@ -25,6 +27,7 @@ class ConfigProvider return [ 'dependencies' => $this->getDependencies(), 'form_elements' => $this->getFormElements(), + 'console' => $this->getConsoleCommands(), ]; } @@ -42,7 +45,15 @@ class ConfigProvider EventManager::class => EventManager::class, ], 'factories' => [ + Command\GeneratePasswordHash::class => Command\GeneratePasswordHashFactory::class, + Hydrator\DoctrineObject::class => Hydrator\DoctrineObjectFactory::class, + + CorsMiddleware::class => Middleware\CorsMiddlewareFactory::class, + JwtAuthentication::class => Middleware\JwtMiddlewareFactory::class, + + Handler\AuthHandler::class => Handler\AuthHandlerFactory::class, + Service\AuthService::class => Service\AuthServiceFactory::class, ], ]; } @@ -61,4 +72,13 @@ class ConfigProvider ], ]; } + + public function getConsoleCommands(): array + { + return [ + 'commands' => [ + Command\GeneratePasswordHash::class, + ] + ]; + } } diff --git a/src/DoctrineExpressiveModule/Form/Element/ElementFactory.php b/src/UtilityModule/Form/Element/ElementFactory.php similarity index 89% rename from src/DoctrineExpressiveModule/Form/Element/ElementFactory.php rename to src/UtilityModule/Form/Element/ElementFactory.php index 0ab0901..56e9b43 100644 --- a/src/DoctrineExpressiveModule/Form/Element/ElementFactory.php +++ b/src/UtilityModule/Form/Element/ElementFactory.php @@ -1,6 +1,6 @@ authService = $authService; + } + + /** + * Respond to POST (login) requests + * @param ServerRequestInterface $request + * @return \Zend\Diactoros\Response\JsonResponse + */ + public function create(ServerRequestInterface $request) + { + $data = $this->getRequestData($request); + try { + return new JsonResponse($this->authService->authenticate( + $data['user'], + $data['pass'] + )); + } catch (NoResultException $e) { + return new JsonResponse("Access denied", 401); + } + } + + /** + * Respond to GET (renew-token) requests + * @param ServerRequestInterface $request + * @return \Zend\Diactoros\Response\JsonResponse + */ + public function get(ServerRequestInterface $request) + { + $token = $request->getAttribute('token'); + return new JsonResponse($this->authService->renewToken($token)); + } +} diff --git a/src/UtilityModule/Handler/AuthHandlerFactory.php b/src/UtilityModule/Handler/AuthHandlerFactory.php new file mode 100644 index 0000000..7bd2f8c --- /dev/null +++ b/src/UtilityModule/Handler/AuthHandlerFactory.php @@ -0,0 +1,18 @@ +get(AuthService::class); + return new AuthHandler($authService); + } +} diff --git a/src/DoctrineExpressiveModule/Hydrator/DoctrineObject.php b/src/UtilityModule/Hydrator/DoctrineObject.php similarity index 99% rename from src/DoctrineExpressiveModule/Hydrator/DoctrineObject.php rename to src/UtilityModule/Hydrator/DoctrineObject.php index 95445e6..c863b2e 100644 --- a/src/DoctrineExpressiveModule/Hydrator/DoctrineObject.php +++ b/src/UtilityModule/Hydrator/DoctrineObject.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator; +namespace UtilityModule\Hydrator; use DateTime; use Doctrine\Common\Persistence\Mapping\ClassMetadata; diff --git a/src/DoctrineExpressiveModule/Hydrator/DoctrineObjectFactory.php b/src/UtilityModule/Hydrator/DoctrineObjectFactory.php similarity index 85% rename from src/DoctrineExpressiveModule/Hydrator/DoctrineObjectFactory.php rename to src/UtilityModule/Hydrator/DoctrineObjectFactory.php index d41e6c0..a9b7133 100644 --- a/src/DoctrineExpressiveModule/Hydrator/DoctrineObjectFactory.php +++ b/src/UtilityModule/Hydrator/DoctrineObjectFactory.php @@ -1,6 +1,6 @@ . */ -namespace DoctrineExpressiveModule\Hydrator\Filter; +namespace UtilityModule\Hydrator\Filter; use Zend\Hydrator\Filter\FilterInterface; diff --git a/src/DoctrineExpressiveModule/Hydrator/Strategy/AbstractCollectionStrategy.php b/src/UtilityModule/Hydrator/Strategy/AbstractCollectionStrategy.php similarity index 98% rename from src/DoctrineExpressiveModule/Hydrator/Strategy/AbstractCollectionStrategy.php rename to src/UtilityModule/Hydrator/Strategy/AbstractCollectionStrategy.php index 2eff700..7500332 100644 --- a/src/DoctrineExpressiveModule/Hydrator/Strategy/AbstractCollectionStrategy.php +++ b/src/UtilityModule/Hydrator/Strategy/AbstractCollectionStrategy.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator\Strategy; +namespace UtilityModule\Hydrator\Strategy; use InvalidArgumentException; use Doctrine\Common\Collections\Collection; diff --git a/src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByReference.php b/src/UtilityModule/Hydrator/Strategy/AllowRemoveByReference.php similarity index 97% rename from src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByReference.php rename to src/UtilityModule/Hydrator/Strategy/AllowRemoveByReference.php index 94c33ef..eff190d 100644 --- a/src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByReference.php +++ b/src/UtilityModule/Hydrator/Strategy/AllowRemoveByReference.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator\Strategy; +namespace UtilityModule\Hydrator\Strategy; /** * When this strategy is used for Collections, if the new collection does not contain elements that are present in diff --git a/src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByValue.php b/src/UtilityModule/Hydrator/Strategy/AllowRemoveByValue.php similarity index 98% rename from src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByValue.php rename to src/UtilityModule/Hydrator/Strategy/AllowRemoveByValue.php index 30a2806..94b7c1d 100644 --- a/src/DoctrineExpressiveModule/Hydrator/Strategy/AllowRemoveByValue.php +++ b/src/UtilityModule/Hydrator/Strategy/AllowRemoveByValue.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator\Strategy; +namespace UtilityModule\Hydrator\Strategy; use Doctrine\Common\Collections\Collection; use LogicException; diff --git a/src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByReference.php b/src/UtilityModule/Hydrator/Strategy/DisallowRemoveByReference.php similarity index 97% rename from src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByReference.php rename to src/UtilityModule/Hydrator/Strategy/DisallowRemoveByReference.php index 47eadc2..b9a325f 100644 --- a/src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByReference.php +++ b/src/UtilityModule/Hydrator/Strategy/DisallowRemoveByReference.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator\Strategy; +namespace UtilityModule\Hydrator\Strategy; /** * When this strategy is used for Collections, if the new collection does not contain elements that are present in diff --git a/src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByValue.php b/src/UtilityModule/Hydrator/Strategy/DisallowRemoveByValue.php similarity index 98% rename from src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByValue.php rename to src/UtilityModule/Hydrator/Strategy/DisallowRemoveByValue.php index 77df2be..50ad014 100644 --- a/src/DoctrineExpressiveModule/Hydrator/Strategy/DisallowRemoveByValue.php +++ b/src/UtilityModule/Hydrator/Strategy/DisallowRemoveByValue.php @@ -17,7 +17,7 @@ * . */ -namespace DoctrineExpressiveModule\Hydrator\Strategy; +namespace UtilityModule\Hydrator\Strategy; use Doctrine\Common\Collections\Collection; use LogicException; diff --git a/src/DoctrineExpressiveModule/Middleware/CorsMiddlewareFactory.php b/src/UtilityModule/Middleware/CorsMiddlewareFactory.php similarity index 88% rename from src/DoctrineExpressiveModule/Middleware/CorsMiddlewareFactory.php rename to src/UtilityModule/Middleware/CorsMiddlewareFactory.php index 2d6143e..798790e 100644 --- a/src/DoctrineExpressiveModule/Middleware/CorsMiddlewareFactory.php +++ b/src/UtilityModule/Middleware/CorsMiddlewareFactory.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace DoctrineExpressiveModule\Middleware; +namespace UtilityModule\Middleware; use Psr\Container\ContainerInterface; use Tuupola\Middleware\CorsMiddleware; diff --git a/src/UtilityModule/Middleware/JwtMiddlewareFactory.php b/src/UtilityModule/Middleware/JwtMiddlewareFactory.php new file mode 100644 index 0000000..441afad --- /dev/null +++ b/src/UtilityModule/Middleware/JwtMiddlewareFactory.php @@ -0,0 +1,69 @@ +get('config'); + /** @var Config $config */ + $config = new Config($configArray); + $this->aclConfig = $config->get('acl_config'); + return new JwtAuthentication([ + "secret" => $this->aclConfig->get('hmac_key'), + "path" => "/api", + "ignore" => $this->getIgnoredRoutes($container), + "secure" => true, + "relaxed" => [ + "localhost", + "granprize.dev.yvan.hu", + ], + "error" => function (ResponseInterface $response, $arguments) { + $data["status"] = "error"; + $data["message"] = $arguments["message"]; + return new JsonResponse($data, 401); + } + ]); + } + + /** + * @param ContainerInterface $container + * @return array + */ + private function getIgnoredRoutes(ContainerInterface $container): array + { + $passThroughRoutes = []; + + /** @var Application $app */ + $app = $container->get(Application::class); + $appRoutes = $app->getRoutes(); + + $unguardedRoutes = $this->aclConfig->get('unguarded_routes', new Config([]))->toArray(); + /** @var Route $route */ + foreach ($appRoutes as $route) { + if (in_array($route->getName(), $unguardedRoutes)) { + $passThroughRoutes[] = $route->getPath(); + } + } + + return $passThroughRoutes; + } +} diff --git a/src/UtilityModule/Service/AuthService.php b/src/UtilityModule/Service/AuthService.php new file mode 100644 index 0000000..e1e634b --- /dev/null +++ b/src/UtilityModule/Service/AuthService.php @@ -0,0 +1,97 @@ +config = $config; + } + + /** + * @param string $user + * @param string $pass + * @return string + * @throws NoResultException + */ + public function authenticate(string $user, string $pass): string + { + $cfgUser = $this->config->get('user'); + $cfgPass = $this->config->get('pass'); + + $crypt = new Bcrypt(); + $crypt->setCost(self::PASSWORD_COST); + + if ($cfgUser===$user && $crypt->verify($pass, $cfgPass)) { + return $this->generateJwt(); + } + + throw new NoResultException(); + } + + /** + * @param $token + * @return string + */ + public function renewToken($token): string + { + return $this->renewJWT($token); + } + + /** + * @return string + */ + private function generateJwt(): string + { + /** @var string $hmacKey */ + $hmacKey = $this->config->get('hmac_key'); + return JWT::encode([ + "iat" => time(), + "nbf" => time(), + "exp" => time() + 3600, + ], $hmacKey); + } + + /** + * @param $token + * @return string + */ + private function renewJWT($token): string + { + /** @var string $hmacKey */ + $hmacKey = $this->config->get('hmac_key'); + return JWT::encode([ + "jti" => $token->jti, + "iat" => time(), + "nbf" => time(), + "exp" => time() + 3600, + ], $hmacKey); + } + + /** + * @param string $password + * @return string + */ + public function createPasswordHash(string $password): string + { + $crypt = new Bcrypt(); + $crypt->setCost(self::PASSWORD_COST); + return $crypt->create($password); + } +} diff --git a/src/UtilityModule/Service/AuthServiceFactory.php b/src/UtilityModule/Service/AuthServiceFactory.php new file mode 100644 index 0000000..75ec6ab --- /dev/null +++ b/src/UtilityModule/Service/AuthServiceFactory.php @@ -0,0 +1,19 @@ +get('config')['acl_config']; + return new AuthService(new Config($configArr)); + } +} diff --git a/src/DoctrineExpressiveModule/Validator/NoObjectExists.php b/src/UtilityModule/Validator/NoObjectExists.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/NoObjectExists.php rename to src/UtilityModule/Validator/NoObjectExists.php diff --git a/src/DoctrineExpressiveModule/Validator/ObjectExists.php b/src/UtilityModule/Validator/ObjectExists.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/ObjectExists.php rename to src/UtilityModule/Validator/ObjectExists.php diff --git a/src/DoctrineExpressiveModule/Validator/Service/AbstractValidatorFactory.php b/src/UtilityModule/Validator/Service/AbstractValidatorFactory.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/Service/AbstractValidatorFactory.php rename to src/UtilityModule/Validator/Service/AbstractValidatorFactory.php diff --git a/src/DoctrineExpressiveModule/Validator/Service/Exception/ServiceCreationException.php b/src/UtilityModule/Validator/Service/Exception/ServiceCreationException.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/Service/Exception/ServiceCreationException.php rename to src/UtilityModule/Validator/Service/Exception/ServiceCreationException.php diff --git a/src/DoctrineExpressiveModule/Validator/Service/NoObjectExistsFactory.php b/src/UtilityModule/Validator/Service/NoObjectExistsFactory.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/Service/NoObjectExistsFactory.php rename to src/UtilityModule/Validator/Service/NoObjectExistsFactory.php diff --git a/src/DoctrineExpressiveModule/Validator/Service/ObjectExistsFactory.php b/src/UtilityModule/Validator/Service/ObjectExistsFactory.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/Service/ObjectExistsFactory.php rename to src/UtilityModule/Validator/Service/ObjectExistsFactory.php diff --git a/src/DoctrineExpressiveModule/Validator/Service/UniqueObjectFactory.php b/src/UtilityModule/Validator/Service/UniqueObjectFactory.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/Service/UniqueObjectFactory.php rename to src/UtilityModule/Validator/Service/UniqueObjectFactory.php diff --git a/src/DoctrineExpressiveModule/Validator/UniqueObject.php b/src/UtilityModule/Validator/UniqueObject.php similarity index 100% rename from src/DoctrineExpressiveModule/Validator/UniqueObject.php rename to src/UtilityModule/Validator/UniqueObject.php