diff --git a/.env b/.env index a00827d5..d1f78e83 100644 --- a/.env +++ b/.env @@ -60,3 +60,7 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=857ce6bfb8d058ca8f51421d40864305d96be4be4a30d75e843b2d13958e6fde ###< lexik/jwt-authentication-bundle ### + +###> nelmio/cors-bundle ### +CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' +###< nelmio/cors-bundle ### diff --git a/composer.json b/composer.json index 6e660bf5..c159ad30 100644 --- a/composer.json +++ b/composer.json @@ -10,16 +10,21 @@ "ext-openssl": ">=7.1", "ext-sodium": "^2.0 | >=7.2", "doctrine/annotations": "*", + "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-fixtures-bundle": "~3.4", + "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^2.13", "endroid/qr-code": "^5.0", "ircmaxell/password-compat": "~1.0.3", "knplabs/knp-menu-bundle": "^3.0", "lexik/jwt-authentication-bundle": "^2.20", "mopa/bootstrap-bundle": "~3.0", + "nelmio/cors-bundle": "^2.4", "nelmio/security-bundle": "^3.0", "pear/crypt_gpg": "^1.6", + "phpdocumentor/reflection-docblock": "^5.3", + "phpstan/phpdoc-parser": "^1.27", "ramsey/uuid": "^4.1", "scheb/2fa-backup-code": "^7.2.0", "scheb/2fa-bundle": "^7.2.0", @@ -27,8 +32,10 @@ "sonata-project/admin-bundle": "^4.0", "sonata-project/doctrine-orm-admin-bundle": "^4.0", "symfony/apache-pack": "^1.0", + "symfony/asset": "6.4.*", "symfony/console": "*", "symfony/dotenv": "*", + "symfony/expression-language": "6.4.*", "symfony/flex": "^1.1", "symfony/form": "*", "symfony/framework-bundle": "*", @@ -37,9 +44,12 @@ "symfony/monolog-bundle": "^3.8.0", "symfony/polyfill-apcu": "^1.0", "symfony/process": "6.4.*", + "symfony/property-access": "6.4.*", + "symfony/property-info": "6.4.*", "symfony/runtime": "6.4.*", "symfony/security-bundle": "*", "symfony/security-csrf": "*", + "symfony/serializer": "6.4.*", "symfony/translation": "*", "symfony/twig-bundle": "6.4.*", "symfony/validator": "*", diff --git a/composer.lock b/composer.lock index 148bed07..105407a9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c9706f16061b30b51ae159d4e070268e", + "content-hash": "161b812d90a5e208e794b2ba17c213fb", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1038,6 +1038,97 @@ ], "time": "2023-11-19T12:48:54+00:00" }, + { + "name": "doctrine/doctrine-migrations-bundle", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1dd42906a5fb9c5960723e2ebb45c68006493835", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.4", + "doctrine/migrations": "^3.2", + "php": "^7.2|^8.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.6 || ^3", + "doctrine/persistence": "^2.0 || ^3 ", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^8.5|^9.5", + "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-symfony": "^3 || ^5", + "symfony/phpunit-bridge": "^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6 || ^7", + "vimeo/psalm": "^4.30 || ^5.15" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\MigrationsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineMigrationsBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "dbal", + "migrations", + "schema" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-migrations-bundle", + "type": "tidelift" + } + ], + "time": "2023-11-13T19:44:41+00:00" + }, { "name": "doctrine/event-manager", "version": "2.0.0", @@ -1367,6 +1458,108 @@ ], "time": "2024-02-05T11:56:58+00:00" }, + { + "name": "doctrine/migrations", + "version": "3.7.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "954e0a314c2f0eb9fb418210445111747de254a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/954e0a314c2f0eb9fb418210445111747de254a6", + "reference": "954e0a314c2f0eb9fb418210445111747de254a6", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/dbal": "^3.5.1 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2.0", + "php": "^8.1", + "psr/log": "^1.1.3 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0" + }, + "conflict": { + "doctrine/orm": "<2.12 || >=4" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3", + "doctrine/sql-formatter": "^1.0", + "ext-pdo_sqlite": "*", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^10.3", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", + "symfony/yaml": "Allows the use of yaml for migration configuration files." + }, + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", + "homepage": "https://www.doctrine-project.org/projects/migrations.html", + "keywords": [ + "database", + "dbal", + "migrations" + ], + "support": { + "issues": "https://github.com/doctrine/migrations/issues", + "source": "https://github.com/doctrine/migrations/tree/3.7.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", + "type": "tidelift" + } + ], + "time": "2024-03-06T13:41:11+00:00" + }, { "name": "doctrine/orm", "version": "2.19.3", @@ -2661,6 +2854,68 @@ }, "time": "2016-12-05T07:27:31+00:00" }, + { + "name": "nelmio/cors-bundle", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioCorsBundle.git", + "reference": "78fcdb91f76b080a1008133def9c7f613833933d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/78fcdb91f76b080a1008133def9c7f613833933d", + "reference": "78fcdb91f76b080a1008133def9c7f613833933d", + "shasum": "" + }, + "require": { + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.6", + "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\CorsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors" + } + ], + "description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application", + "keywords": [ + "api", + "cors", + "crossdomain" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.4.0" + }, + "time": "2023-11-30T16:41:19+00:00" + }, { "name": "nelmio/security-bundle", "version": "v3.2.0", @@ -2991,6 +3246,174 @@ }, "time": "2021-03-21T15:43:46+00:00" }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, { "name": "phpstan/phpdoc-parser", "version": "1.27.0", @@ -8827,6 +9250,68 @@ ], "time": "2023-12-26T14:02:43+00:00" }, + { + "name": "symfony/stopwatch", + "version": "v6.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "416596166641f1f728b0a64f5b9dd07cceb410c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/416596166641f1f728b0a64f5b9dd07cceb410c1", + "reference": "416596166641f1f728b0a64f5b9dd07cceb410c1", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T14:35:58+00:00" + }, { "name": "symfony/string", "version": "v6.4.4", @@ -10071,6 +10556,64 @@ "source": "https://github.com/ua-parser/uap-php/tree/v3.9.14" }, "time": "2020-10-02T23:36:20+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [ @@ -12747,68 +13290,6 @@ ], "time": "2024-02-08T19:22:56+00:00" }, - { - "name": "symfony/stopwatch", - "version": "v6.4.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "416596166641f1f728b0a64f5b9dd07cceb410c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/416596166641f1f728b0a64f5b9dd07cceb410c1", - "reference": "416596166641f1f728b0a64f5b9dd07cceb410c1", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/service-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:35:58+00:00" - }, { "name": "symfony/web-profiler-bundle", "version": "v6.4.4", diff --git a/config/bundles.php b/config/bundles.php index 79937ed5..07baa90b 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -22,4 +22,6 @@ Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], + Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], + Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], ]; diff --git a/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations.yaml new file mode 100644 index 00000000..29231d94 --- /dev/null +++ b/config/packages/doctrine_migrations.yaml @@ -0,0 +1,6 @@ +doctrine_migrations: + migrations_paths: + # namespace is arbitrary but should be different from App\Migrations + # as migrations classes should NOT be autoloaded + 'DoctrineMigrations': '%kernel.project_dir%/migrations' + enable_profiler: false diff --git a/config/packages/lexik_jwt_authentication.yaml b/config/packages/lexik_jwt_authentication.yaml index edfb69dc..bdc9c063 100644 --- a/config/packages/lexik_jwt_authentication.yaml +++ b/config/packages/lexik_jwt_authentication.yaml @@ -2,3 +2,12 @@ lexik_jwt_authentication: secret_key: '%env(resolve:JWT_SECRET_KEY)%' public_key: '%env(resolve:JWT_PUBLIC_KEY)%' pass_phrase: '%env(JWT_PASSPHRASE)%' + token_extractors: + authorization_header: + enabled: true + prefix: Bearer + name: Authorization + +when@prod: + lexik_jwt_authentication: + token_ttl: 31536000 \ No newline at end of file diff --git a/config/packages/nelmio_cors.yaml b/config/packages/nelmio_cors.yaml new file mode 100644 index 00000000..c7665081 --- /dev/null +++ b/config/packages/nelmio_cors.yaml @@ -0,0 +1,10 @@ +nelmio_cors: + defaults: + origin_regex: true + allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] + allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] + allow_headers: ['Content-Type', 'Authorization'] + expose_headers: ['Link'] + max_age: 3600 + paths: + '^/': null diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 28add15a..48de0a3f 100755 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -105,7 +105,7 @@ security: pattern: ^/(_(profiler|error|wdt)|css|images|js)/ security: false api-login: - pattern: ^/api/login + pattern: ^/api/user/login stateless: false json_login: check_path: api_login @@ -167,5 +167,7 @@ security: - { path: "^/alias", roles: ROLE_USER, allow_if: "!is_granted('ROLE_SPAM')"} - { path: "^/account", roles: ROLE_USER, allow_if: "!is_granted('ROLE_SPAM')"} - { path: "^/admin", roles: ROLE_DOMAIN_ADMIN } - - { path: "^/api/login", roles: PUBLIC_ACCESS } - - { path: "^/api", roles: ROLE_USER } \ No newline at end of file + - { path: "^/api/user/login", roles: PUBLIC_ACCESS } + - { path: "^/api/user/login/2fa", roles: IS_AUTHENTICATED_2FA_IN_PROGRESS } + - { path: "^/api/user/register", roles: PUBLIC_ACCESS } + - { path: "^/api/user/", roles: ROLE_USER } diff --git a/migrations/.gitignore b/migrations/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/src/Controller/Api/AliasController.php b/src/Controller/Api/AliasController.php new file mode 100644 index 00000000..2726dacd --- /dev/null +++ b/src/Controller/Api/AliasController.php @@ -0,0 +1,41 @@ +security->getUser(); + + $aliases = $this->aliasHandler->getEnabledAliasesByUser($user); + $data = []; + if ($aliases) { + foreach ($aliases as $alias) { + $data[] = [ + 'id' => $alias->getId(), + 'address' => $alias->getSource(), + 'isRandom' => $alias->isRandom(), + ]; + } + } + return $this->json($data, 200); + } +} diff --git a/src/Controller/ApiLoginController.php b/src/Controller/Api/LoginController.php similarity index 74% rename from src/Controller/ApiLoginController.php rename to src/Controller/Api/LoginController.php index 9ba502da..f8024b58 100644 --- a/src/Controller/ApiLoginController.php +++ b/src/Controller/Api/LoginController.php @@ -1,6 +1,6 @@ createAccessDeniedException("User not in 2fa process"); $jsonResponse = new Response(json_encode($error), Response::HTTP_BAD_REQUEST); $jsonResponse->headers->set('Content-Type', 'application/json'); return $jsonResponse; } - } } diff --git a/src/Controller/Api/UserController.php b/src/Controller/Api/UserController.php new file mode 100644 index 00000000..18d3bb9b --- /dev/null +++ b/src/Controller/Api/UserController.php @@ -0,0 +1,30 @@ +security->getUser(); + + $data=[ + 'username' => $user->getEmail(), + 'mailcrypt_enabled' => $user->hasMailCrypt(), + 'totp_enabled' => $user->isTotpAuthenticationEnabled(), + ]; + + return $this->json($data, 200); + } +} \ No newline at end of file diff --git a/src/Controller/Api/VoucherController.php b/src/Controller/Api/VoucherController.php new file mode 100644 index 00000000..4e26d5ca --- /dev/null +++ b/src/Controller/Api/VoucherController.php @@ -0,0 +1,39 @@ +security->getUser(); + + $vouchers = $this->voucherHandler->getVouchersByUser($user); + + $data = []; + if ($vouchers) { + foreach ($vouchers as $voucher) { + $data[] = [ + 'code' => $voucher->getCode(), + ]; + } + } + return $this->json($data, 200); + } +} diff --git a/src/Controller/Api/WkdController.php b/src/Controller/Api/WkdController.php new file mode 100644 index 00000000..c4203fa3 --- /dev/null +++ b/src/Controller/Api/WkdController.php @@ -0,0 +1,45 @@ +security->getUser(); + + $openpgpkeys = $this->wkdHandler->getOpenPgpKeysByUser($user); + $data = []; + if ($openpgpkeys) { + foreach ($openpgpkeys as $openpgpkey) { + $data[] = [ + 'uid' => $openpgpkey->getEmail(), + 'short' => $openpgpkey->getKeyId(), + 'long' => $openpgpkey->getKeyFingerprint(), + 'expireTime' => $openpgpkey->getKeyExpireTime(), + ]; + } + } + return $this->json($data, 200); + } +} diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php index 16613e19..55c39fed 100644 --- a/src/Controller/RegistrationController.php +++ b/src/Controller/RegistrationController.php @@ -133,4 +133,5 @@ public function register(Request $request, string $voucher = null): Response return $this->render('Registration/register.html.twig', ['form' => $form->createView()]); } + } diff --git a/src/Doctrine/ApiQueryExtension.php b/src/Doctrine/ApiQueryExtension.php new file mode 100644 index 00000000..80a028c1 --- /dev/null +++ b/src/Doctrine/ApiQueryExtension.php @@ -0,0 +1,60 @@ +security->getUser()) { + return; + } + $this->filterEntity($queryBuilder, $resourceClass, $this->security->getUser()); + } + + public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void + { + // Same filters as for collections + $this->applyToCollection($queryBuilder, $queryNameGenerator, $resourceClass, $operation, $context); + } + + private function filterEntity(QueryBuilder $queryBuilder, string $resourceClass, User $user): void + { + $rootAlias = $queryBuilder->getRootAliases()[0]; + if (User::class === $resourceClass) { + $queryBuilder->andWhere(sprintf('%s.id = :current_user', $rootAlias)); + $queryBuilder->setParameter('current_user', $user->getId()); + } else if ( + (OpenPgpKey::class === $resourceClass) || + (Alias::class === $resourceClass) || + (Voucher::class === $resourceClass) + ) { + $queryBuilder->andWhere(sprintf('%s.user = :current_user', $rootAlias)); + $queryBuilder->setParameter('current_user', $user->getId()); + } + // additional contraints for vouchers + if (Voucher::class === $resourceClass) { + $queryBuilder->andWhere(sprintf('%s.redeemedTime is NULL', $rootAlias)); + } + // additional constraints for aliases + if (Alias::class === $resourceClass) { + $queryBuilder->andWhere(sprintf('%s.deleted = false', $rootAlias)); + } + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 08313bcb..83a4a5a7 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -29,12 +29,12 @@ use App\Traits\TwofactorTrait; use App\Traits\UpdatedTimeTrait; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\Mapping\Index; use Scheb\TwoFactorBundle\Model\BackupCodeInterface; use Scheb\TwoFactorBundle\Model\Totp\TwoFactorInterface; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherAwareInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; -use Doctrine\ORM\Mapping\Index; #[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Table(name: 'virtual_users')] @@ -120,7 +120,8 @@ public function getUsername(): ?string /** * @return string */ - public function getUserIdentifier(): string { + public function getUserIdentifier(): string + { return $this->email; } diff --git a/src/Handler/AliasHandler.php b/src/Handler/AliasHandler.php index 1aaca3ef..7269bcbb 100644 --- a/src/Handler/AliasHandler.php +++ b/src/Handler/AliasHandler.php @@ -48,4 +48,14 @@ public function create(User $user, ?string $localPart = null): ?Alias return null; } + + /** + * @return Alias[] + */ + public function getEnabledAliasesByUser(User $user): array + { + $aliases = $this->repository->findByUser($user, null, false); + + return $aliases; + } } diff --git a/src/Handler/WkdHandler.php b/src/Handler/WkdHandler.php index 9b20b2f9..a5486072 100644 --- a/src/Handler/WkdHandler.php +++ b/src/Handler/WkdHandler.php @@ -2,6 +2,7 @@ namespace App\Handler; +use App\Entity\Alias; use App\Entity\OpenPgpKey; use App\Entity\User; use App\Exception\MultipleGpgKeysForUserException; @@ -9,6 +10,7 @@ use App\Exception\NoGpgKeyForUserException; use App\Importer\GpgKeyImporter; use App\Repository\OpenPgpKeyRepository; +use App\Repository\AliasRepository; use Doctrine\ORM\EntityManagerInterface; use RuntimeException; use Tuupola\Base32; @@ -16,6 +18,8 @@ class WkdHandler { private readonly OpenPgpKeyRepository $repository; + private readonly AliasRepository $aliasRepository; + /** * WkdHandler constructor. @@ -25,6 +29,8 @@ public function __construct(private readonly EntityManagerInterface $manager, private readonly string $wkdFormat) { $this->repository = $manager->getRepository(OpenPgpKey::class); + $this->aliasRepository = $manager->getRepository(Alias::class); + } private function getWkdPath(string $domain): string @@ -138,4 +144,29 @@ public function getDomainWkdPath(string $domain): string { return $this->getWkdPath($domain); } -} + + /** + * @return OpenPgpKey[] + */ + public function getOpenPgpKeysByUser(User $user): array + { + $uids = array($user->getEmail()); + + // get all non-random, non-deleted aliases of a user + $aliases = $this->aliasRepository->findByUser($user, null, false); + if ($aliases) { + foreach ($aliases as $alias) { + array_push($uids, $alias->getSource()); + } + } + + $openpgpkeys = array(); + foreach ($uids as $uid) { + if (null !== $openpgpkey = $this->repository->findByEmail($uid)) { + array_push($openpgpkeys, $openpgpkey); + }; + } + + return $openpgpkeys; + } +} \ No newline at end of file diff --git a/src/Traits/AliasAwareTrait.php b/src/Traits/AliasAwareTrait.php index f3466a4f..f4d6ca44 100644 --- a/src/Traits/AliasAwareTrait.php +++ b/src/Traits/AliasAwareTrait.php @@ -2,8 +2,10 @@ namespace App\Traits; + use App\Entity\Alias; + trait AliasAwareTrait { private ?Alias $alias = null; diff --git a/src/Traits/MailCryptTrait.php b/src/Traits/MailCryptTrait.php index 0dcb0213..a4f48d2e 100644 --- a/src/Traits/MailCryptTrait.php +++ b/src/Traits/MailCryptTrait.php @@ -4,6 +4,7 @@ use Doctrine\ORM\Mapping as ORM; + trait MailCryptTrait { #[ORM\Column(options: ['default' => false])] diff --git a/src/Traits/OpenPgpKeyTrait.php b/src/Traits/OpenPgpKeyTrait.php index 22226915..5b55d921 100644 --- a/src/Traits/OpenPgpKeyTrait.php +++ b/src/Traits/OpenPgpKeyTrait.php @@ -5,6 +5,7 @@ use DateTime; use Doctrine\ORM\Mapping as ORM; + trait OpenPgpKeyTrait { #[ORM\Column(type: 'text')] diff --git a/src/Traits/QuotaTrait.php b/src/Traits/QuotaTrait.php index eab563fd..7890fe5a 100644 --- a/src/Traits/QuotaTrait.php +++ b/src/Traits/QuotaTrait.php @@ -4,6 +4,7 @@ use Doctrine\ORM\Mapping as ORM; + trait QuotaTrait { #[ORM\Column(nullable: true)] diff --git a/src/Traits/RandomTrait.php b/src/Traits/RandomTrait.php index fba15fc8..ace155cc 100644 --- a/src/Traits/RandomTrait.php +++ b/src/Traits/RandomTrait.php @@ -4,8 +4,11 @@ use Doctrine\ORM\Mapping as ORM; + + trait RandomTrait { + #[ORM\Column(options: ['default' => false])] private bool $random = false; diff --git a/src/Traits/TwofactorTrait.php b/src/Traits/TwofactorTrait.php index d9250c6c..80c7f617 100644 --- a/src/Traits/TwofactorTrait.php +++ b/src/Traits/TwofactorTrait.php @@ -6,6 +6,7 @@ use Scheb\TwoFactorBundle\Model\Totp\TotpConfiguration; use Scheb\TwoFactorBundle\Model\Totp\TotpConfigurationInterface; + trait TwofactorTrait { #[ORM\Column(nullable: true)] diff --git a/src/Traits/UserAwareTrait.php b/src/Traits/UserAwareTrait.php index 8785706d..6d2992a1 100644 --- a/src/Traits/UserAwareTrait.php +++ b/src/Traits/UserAwareTrait.php @@ -5,6 +5,8 @@ use App\Entity\User; use Doctrine\ORM\Mapping as ORM; + + trait UserAwareTrait { #[ORM\ManyToOne(targetEntity: \User::class)] diff --git a/symfony.lock b/symfony.lock index 7d0696d2..657a11af 100644 --- a/symfony.lock +++ b/symfony.lock @@ -71,6 +71,19 @@ "src/DataFixtures/AppFixtures.php" ] }, + "doctrine/doctrine-migrations-bundle": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.1", + "ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33" + }, + "files": [ + "config/packages/doctrine_migrations.yaml", + "migrations/.gitignore" + ] + }, "doctrine/event-manager": { "version": "v1.0.0" }, @@ -146,6 +159,18 @@ "mopa/composer-bridge": { "version": "v1.5.0" }, + "nelmio/cors-bundle": { + "version": "2.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.5", + "ref": "6bea22e6c564fba3a1391615cada1437d0bde39c" + }, + "files": [ + "config/packages/nelmio_cors.yaml" + ] + }, "nelmio/security-bundle": { "version": "2.4", "recipe": {