diff --git a/composer.json b/composer.json index 06498fa95c66c33c75334502de55b673af7afe5f..c6872f52c0eb49da62467063e73c591f04440d44 100644 --- a/composer.json +++ b/composer.json @@ -6,12 +6,15 @@ "php": "^7.3", "api-platform/core": "^2.5", "symfony/framework-bundle": "^4.1.12", - "dbp/api-core-bundle": "dev-main" + "dbp/api-core-bundle": "dev-main", + "ext-json": "*" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", "phpstan/phpstan": "^0.12.33", "phpstan/phpstan-phpunit": "^0.12.13", + "symfony/browser-kit": "^4.4", + "symfony/http-client": "^4.4", "symfony/phpunit-bridge": "^4.4", "vimeo/psalm": "^4.2.1" }, @@ -64,4 +67,4 @@ "@php vendor/bin/simple-phpunit --coverage-html _coverage" ] } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 123b7ecd04c7a8cab0b57f520e6d7f7d80701f09..7b9868f531e8b878eefe4ed85f84d97a23f3a7c7 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": "aacafd2d2227e4937544cb12b9111f79", + "content-hash": "7042f009a8911b77ab5f7b5e030dae86", "packages": [ { "name": "api-platform/core", @@ -224,7 +224,7 @@ "source": { "type": "git", "url": "git@gitlab.tugraz.at:dbp/middleware/dbp-api/api-core-bundle.git", - "reference": "db2c221338c234b762192da66bf9d9f33b4e2662" + "reference": "3d6767a82ed493a37bd7763e5cfb34e2dac1b1a7" }, "require": { "api-platform/core": "<2.6", @@ -300,7 +300,7 @@ "license": [ "AGPL-3.0-or-later" ], - "time": "2021-02-22T10:43:38+00:00" + "time": "2021-02-22T12:56:47+00:00" }, { "name": "doctrine/annotations", @@ -6313,16 +6313,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "v1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541" + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/85e83cacd2ed573238678c6875f8f0d7ec699541", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", "shasum": "" }, "require": { @@ -6363,9 +6363,9 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.0" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" }, - "time": "2020-10-23T13:55:30+00:00" + "time": "2021-02-22T14:02:09+00:00" }, { "name": "friendsofphp/php-cs-fixer", @@ -6866,6 +6866,77 @@ ], "time": "2020-10-26T13:10:38+00:00" }, + { + "name": "symfony/browser-kit", + "version": "v4.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "f6f060bdc473c3f3b1f00e2ebdeb3d02eda77f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f6f060bdc473c3f3b1f00e2ebdeb3d02eda77f82", + "reference": "f6f060bdc473c3f3b1f00e2ebdeb3d02eda77f82", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/dom-crawler": "^3.4|^4.0|^5.0" + }, + "require-dev": { + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/http-client": "^4.3|^5.0", + "symfony/mime": "^4.3|^5.0", + "symfony/process": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "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": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v4.4.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T09:09:26+00:00" + }, { "name": "symfony/console", "version": "v4.4.19", @@ -6955,6 +7026,160 @@ ], "time": "2021-01-27T09:09:26+00:00" }, + { + "name": "symfony/dom-crawler", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "5d89ceb53ec65e1973a555072fac8ed5ecad3384" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/5d89ceb53ec65e1973a555072fac8ed5ecad3384", + "reference": "5d89ceb53ec65e1973a555072fac8ed5ecad3384", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.15" + }, + "conflict": { + "masterminds/html5": "<2.6" + }, + "require-dev": { + "masterminds/html5": "^2.6", + "symfony/css-selector": "^4.4|^5.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "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": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v5.2.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": "2021-01-27T10:01:46+00:00" + }, + { + "name": "symfony/http-client", + "version": "v4.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "d8df50fe9229576b254c6822eb5cfff36c02c967" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/d8df50fe9229576b254c6822eb5cfff36c02c967", + "reference": "d8df50fe9229576b254c6822eb5cfff36c02c967", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "psr/log": "^1.0", + "symfony/http-client-contracts": "^1.1.10|^2", + "symfony/polyfill-php73": "^1.11", + "symfony/service-contracts": "^1.0|^2" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "1.1" + }, + "require-dev": { + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.3|^5.0", + "symfony/http-kernel": "^4.4.13", + "symfony/process": "^4.2|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v4.4.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T09:09:26+00:00" + }, { "name": "symfony/options-resolver", "version": "v5.2.3", @@ -7538,7 +7763,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.3" + "php": "^7.3", + "ext-json": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d908cdda3125109f1bf4bbc1d24a7ade330a4191..c8117a0e3c572c076b80502205dc3ce8a670c458 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,6 +13,8 @@ <server name="SHELL_VERBOSITY" value="-1"/> <server name="SYMFONY_PHPUNIT_REMOVE" value=""/> <server name="SYMFONY_PHPUNIT_VERSION" value="9"/> + <server name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/> + <server name="KERNEL_CLASS" value="DBP\API\StarterBundle\Tests\Kernel"/> </php> <testsuites> <testsuite name="Project Test Suite"> diff --git a/src/Controller/LoggedInOnly.php b/src/Controller/LoggedInOnly.php new file mode 100644 index 0000000000000000000000000000000000000000..18b5785470b474791f401999c0187c351dc1a1b8 --- /dev/null +++ b/src/Controller/LoggedInOnly.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace DBP\API\StarterBundle\Controller; + +use DBP\API\StarterBundle\Entity\Place; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; + +class LoggedInOnly extends AbstractController +{ + public function __invoke(Place $data, Request $request): Place + { + $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); + + return $data; + } +} diff --git a/src/Entity/Place.php b/src/Entity/Place.php index 8f981fdf0be4ac1e88a1103138c5e43c50a7affb..74866c4f13e4a6df3ef3b7d9f80818fa0d8aadbb 100644 --- a/src/Entity/Place.php +++ b/src/Entity/Place.php @@ -6,6 +6,7 @@ namespace DBP\API\StarterBundle\Entity; use ApiPlatform\Core\Annotation\ApiProperty; use ApiPlatform\Core\Annotation\ApiResource; +use DBP\API\StarterBundle\Controller\LoggedInOnly; use Symfony\Component\Serializer\Annotation\Groups; /** @@ -16,7 +17,14 @@ use Symfony\Component\Serializer\Annotation\Groups; * itemOperations={ * "get", * "put", - * "delete" + * "delete", + * "loggedin_only" = { + * "security" = "is_granted('IS_AUTHENTICATED_FULLY')", + * "method" = "GET", + * "path" = "/places/{id}/loggedin-only", + * "controller" = LoggedInOnly::class, + * "openapi_context" = {"summary" = "Only works when logged in."}, + * } * }, * iri="https://schema.org/Place", * normalizationContext={ diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 2eec657704474839bfd63410d12fca609ac866d0..9916eaff27edc9a20b07309c6599c780425b358f 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -3,6 +3,11 @@ services: autowire: true autoconfigure: true + DBP\API\StarterBundle\Controller\: + resource: '../../Controller' + tags: ['controller.service_arguments'] + autowire: true + DBP\API\StarterBundle\DataProvider\PlaceCollectionDataProvider: tags: [{ name: 'api_platform.collection_data_provider'}] autowire: true diff --git a/tests/ApiTest.php b/tests/ApiTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1fc411a641606a21c1571c241014480ce3e9fb6a --- /dev/null +++ b/tests/ApiTest.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +namespace DBP\API\StarterBundle\Tests; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase; +use Symfony\Component\HttpFoundation\Response; + +class ApiTest extends ApiTestCase +{ + public function testBasics() + { + $client = self::createClient(); + $response = $client->request('GET', '/places'); + $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); + + $response = $client->request('GET', '/places/graz'); + $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); + + $response = $client->request('DELETE', '/places/graz'); + $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); + + $response = $client->request('PUT', '/places/graz', [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => json_encode(['name' => 'foo']), + ]); + $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); + $this->assertSame('foo', json_decode($response->getContent(), true)['name']); + } + + public function testNoAuth() + { + $client = self::createClient(); + $response = $client->request('GET', '/places/graz/loggedin-only'); + $this->assertSame(Response::HTTP_UNAUTHORIZED, $response->getStatusCode()); + } +} diff --git a/tests/Kernel.php b/tests/Kernel.php new file mode 100644 index 0000000000000000000000000000000000000000..4c9936ea5b8fe1033872d2156772c43dfce4d416 --- /dev/null +++ b/tests/Kernel.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +namespace DBP\API\StarterBundle\Tests; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle; +use DBP\API\CoreBundle\DbpCoreBundle; +use DBP\API\StarterBundle\DbpStarterBundle; +use Nelmio\CorsBundle\NelmioCorsBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel as BaseKernel; +use Symfony\Component\Routing\RouteCollectionBuilder; + +class Kernel extends BaseKernel +{ + use MicroKernelTrait; + + public function registerBundles(): iterable + { + yield new FrameworkBundle(); + yield new SecurityBundle(); + yield new TwigBundle(); + yield new NelmioCorsBundle(); + yield new ApiPlatformBundle(); + yield new DbpStarterBundle(); + yield new DbpCoreBundle(); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + $routes->import('@DbpCoreBundle/Resources/config/routing.yaml'); + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) + { + $c->loadFromExtension('framework', [ + 'test' => true, + ]); + } +}